1/* ======================================================================== 2* Extends Bootstrap v3.1.1 3 4* Copyright (c) <2015> eBay Software Foundation 5 6* All rights reserved. 7 8* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 10* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 12* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 13 14* Neither the name of eBay or any of its subsidiaries or affiliates nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 15 16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 17 18* ======================================================================== 19* 20* Updated 02/05/2019 by Matt Baer - Added an id to the title inside of carousel caption 21* Updated 02/05/2019 by Matt Baer - Moved carousel play/pause button to top of carousel div container 22* Updated 04/22/2019 by Matt Baer - No longer add play/pause button if there is only one slide. Also don't add labelledby if there is no header for tabs/carousels,etc. 23* Updated 05/01/2022 by Matt Baer - Moved role=menuitem to the <li> instead of the inner <a> element. 24* 25* */ 26 27 28(function($) { 29 "use strict"; 30 31 // GENERAL UTILITY FUNCTIONS 32 // =============================== 33 34 var uniqueId = function(prefix) { 35 return (prefix || 'ui-id') + '-' + Math.floor((Math.random()*1000)+1) 36 } 37 38 39 var removeMultiValAttributes = function (el, attr, val) { 40 var describedby = (el.attr( attr ) || "").split( /\s+/ ) 41 , index = $.inArray(val, describedby) 42 if ( index !== -1 ) { 43 describedby.splice( index, 1 ) 44 } 45 describedby = $.trim( describedby.join( " " ) ) 46 if (describedby ) { 47 el.attr( attr, describedby ) 48 } else { 49 el.removeAttr( attr ) 50 } 51 } 52 53// selectors Courtesy: https://github.com/jquery/jquery-ui/blob/master/ui/core.js 54 var focusable = function ( element, isTabIndexNotNaN ) { 55 var map, mapName, img, 56 nodeName = element.nodeName.toLowerCase(); 57 if ( "area" === nodeName ) { 58 map = element.parentNode; 59 mapName = map.name; 60 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { 61 return false; 62 } 63 img = $( "img[usemap='#" + mapName + "']" )[ 0 ]; 64 return !!img && visible( img ); 65 } 66 return ( /input|select|textarea|button|object/.test( nodeName ) ? 67 !element.disabled : 68 "a" === nodeName ? 69 element.href || isTabIndexNotNaN :isTabIndexNotNaN) && visible( element ); // the element and all of its ancestors must be visible 70 } 71 var visible = function ( element ) { 72 return $.expr.filters.visible( element ) && 73 !$( element ).parents().addBack().filter(function() { 74 return $.css( this, "visibility" ) === "hidden"; 75 }).length; 76 } 77 78 $.extend( $.expr[ ":" ], { 79 data: $.expr.createPseudo ? 80 $.expr.createPseudo(function( dataName ) { 81 return function( elem ) { 82 return !!$.data( elem, dataName ); 83 }; 84 }) : 85 // support: jQuery <1.8 86 function( elem, i, match ) { 87 return !!$.data( elem, match[ 3 ] ); 88 }, 89 90 focusable: function( element ) { 91 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); 92 }, 93 94 tabbable: function( element ) { 95 var tabIndex = $.attr( element, "tabindex" ), 96 isTabIndexNaN = isNaN( tabIndex ); 97 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); 98 } 99 }); 100 101 // Modal Extension 102 // =============================== 103 104 $('.modal-dialog').attr( {'role' : 'document'}) 105 var modalhide = $.fn.modal.Constructor.prototype.hide 106 $.fn.modal.Constructor.prototype.hide = function(){ 107 var modalOpener = this.$element.parent().find('[data-target="#' + this.$element.attr('id') + '"]') 108 modalhide.apply(this, arguments)
109 modalOpener.focus() 110 $(document).off('keydown.bs.modal') 111 } 112 113 var modalfocus = $.fn.modal.Constructor.prototype.enforceFocus 114 $.fn.modal.Constructor.prototype.enforceFocus = function(){ 115 var focEls = this.$element.find(":tabbable") 116 , lastEl = focEls[focEls.length-1] 117 $(document).on('keydown.bs.modal', $.proxy(function (ev) { 118 if(!this.$element.has(ev.target).length && ev.shiftKey && ev.keyCode === 9) { 119 lastEl.focus() 120 ev.preventDefault(); 121 } 122 }, this)) 123 124 modalfocus.apply(this, arguments) 125 } 126 // DROPDOWN Extension 127 // =============================== 128 129 var toggle = '[data-toggle=dropdown]' 130 , $par 131 , firstItem 132 , focusDelay = 200 133 , menus = $(toggle).parent().find('ul').attr('role','menu') 134 , lis = menus.find('li').attr('role','menuitem') 135 136 lis.find('a').attr({'tabIndex':'-1'}) 137 $(toggle).attr({ 'aria-haspopup':'true', 'aria-expanded': 'false'}) 138 139 $(toggle).parent().on('shown.bs.dropdown',function(e){ 140 $par = $(this) 141 var $toggle = $par.find(toggle) 142 $toggle.attr('aria-expanded','true') 143 $toggle.on('keydown.bs.dropdown', $.proxy(function (ev) { 144 setTimeout(function(){ 145 firstItem = $('.dropdown-menu [role=menuitem]:visible a', $par)[0] 146 try{ firstItem.focus()} catch(ex) {} 147 }, focusDelay) 148 }, this)) 149 150 }) 151 152 $(toggle).parent().on('hidden.bs.dropdown',function(e){ 153 $par = $(this) 154 var $toggle = $par.find(toggle) 155 $toggle.attr('aria-expanded','false') 156 }) 157 158 $(document) 159 .on('focusout.dropdown.data-api', '.dropdown-menu', function(e){ 160 var $this = $(this) 161 , that = this 162 setTimeout(function() { 163 if(!$.contains(that, document.activeElement)){ 164 $this.parent().removeClass('open') 165 $this.parent().find('[data-toggle=dropdown]').attr('aria-expanded','false') 166 } 167 }, 150) 168 }) 169 .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , $.fn.dropdown.Constructor.prototype.keydown) 170 // Tab Extension 171 // =============================== 172 173 var $tablist = $('.nav-tabs, .nav-pills') 174 , $lis = $tablist.children('li') 175 , $tabs = $tablist.find('[data-toggle="tab"], [data-toggle="pill"]') 176 177 if($tabs){ 178 $tablist.attr('role', 'tablist') 179 $lis.attr('role', 'presentation') 180 $tabs.attr('role', 'tab') 181 } 182 183 $tabs.each(function( index ) { 184 var tabpanel = $($(this).attr('href')) 185 , tab = $(this) 186 , tabid = tab.attr('id') || uniqueId('ui-tab') 187 188 tab.attr('id', tabid) 189 190 if(tab.parent().hasClass('active')){ 191 tab.attr( { 'tabIndex' : '0', 'aria-selected' : 'true', 'aria-controls': tab.attr('href').substr(1) } ) 192 tabpanel.attr({ 'role' : 'tabpanel', 'tabIndex' : '0', 'aria-hidden' : 'false' }) 193 } else{ 194 tab.attr( { 'tabIndex' : '-1', 'aria-selected' : 'false', 'aria-controls': tab.attr('href').substr(1) } ) 195 tabpanel.attr( { 'role' : 'tabpanel', 'tabIndex' : '-1', 'aria-hidden' : 'true' } ) 196 } 197 198 if($("#"+tabid).length > 0) { 199 collpanel.attr('aria-labelledby', tabid); 200 } 201 }) 202 203 $.fn.tab.Constructor.prototype.keydown = function (e) { 204 var $this = $(this) 205 , $items 206 , $ul = $this.closest('ul[role=tablist] ') 207 , index 208 , k = e.which || e.keyCode 209
210 $this = $(this) 211 if (!/(37|38|39|40)/.test(k)) return 212 213 $items = $ul.find('[role=tab]:visible') 214 index = $items.index($items.filter(':focus')) 215 216 if (k == 38 || k == 37) index-- // up & left 217 if (k == 39 || k == 40) index++ // down & right 218 219 220 if(index < 0) index = $items.length -1 221 if(index == $items.length) index = 0 222 223 var nextTab = $items.eq(index) 224 if(nextTab.attr('role') ==='tab'){ 225 226 nextTab.tab('show') //Comment this line for dynamically loaded tabPabels, to save Ajax requests on arrow key navigation 227 .focus() 228 } 229 // nextTab.focus() 230 231 e.preventDefault() 232 e.stopPropagation() 233 } 234 235 $(document).on('keydown.tab.data-api','[data-toggle="tab"], [data-toggle="pill"]' , $.fn.tab.Constructor.prototype.keydown) 236 237 var tabactivate = $.fn.tab.Constructor.prototype.activate; 238 $.fn.tab.Constructor.prototype.activate = function (element, container, callback) { 239 var $active = container.find('> .active') 240 $active.find('[data-toggle=tab], [data-toggle=pill]').attr({ 'tabIndex' : '-1','aria-selected' : false }) 241 $active.filter('.tab-pane').attr({ 'aria-hidden' : true,'tabIndex' : '-1' }) 242 243 tabactivate.apply(this, arguments) 244 245 element.addClass('active') 246 element.find('[data-toggle=tab], [data-toggle=pill]').attr({ 'tabIndex' : '0','aria-selected' : true }) 247 element.filter('.tab-pane').attr({ 'aria-hidden' : false,'tabIndex' : '0' }) 248 } 249 250 // Collapse Extension 251 // =============================== 252 253 var $colltabs = $('[data-toggle="collapse"]') 254 $colltabs.each(function( index ) { 255 var colltab = $(this) 256 , collpanel = (colltab.attr('data-target')) ? $(colltab.attr('data-target')) : $(colltab.attr('href')) 257 , parent = colltab.attr('data-parent') 258 , collparent = parent && $(parent) 259 , collid = colltab.attr('id') || uniqueId('ui-collapse') 260 261 colltab.attr('id', collid) 262 263 if(collparent){ 264 colltab.attr({ 'role':'tab', 'aria-selected':'false', 'aria-expanded':'false' }) 265 $(collparent).find('div:not(.collapse,.panel-body), h4').attr('role','presentation') 266 collparent.attr({ 'role' : 'tablist', 'aria-multiselectable' : 'true' }) 267 268 if(collpanel.hasClass('in')){ 269 colltab.attr({ 'aria-controls': collpanel.attr('id'), 'aria-selected':'true', 'aria-expanded':'true', 'tabindex':'0' }) 270 collpanel.attr({ 'role':'tabpanel', 'tabindex':'0', 'aria-hidden':'false' }) 271 }else{ 272 colltab.attr({'aria-controls' : collpanel.attr('id'), 'tabindex':'-1' }) 273 collpanel.attr({ 'role':'tabpanel', 'tabindex':'-1', 'aria-hidden':'true' }) 274 } 275 if($("#"+collid).length > 0) { 276 collpanel.attr('aria-labelledby', collid); 277 } 278 } 279 }) 280 281 var collToggle = $.fn.collapse.Constructor.prototype.toggle 282 $.fn.collapse.Constructor.prototype.toggle = function(){ 283 var prevTab = this.$parent && this.$parent.find('[aria-expanded="true"]') , href 284 285 if(prevTab){ 286 var prevPanel = prevTab.attr('data-target') || (href = prevTab.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') 287 , $prevPanel = $(prevPanel) 288 , $curPanel = this.$element 289 , par = this.$parent 290 , curTab 291 292 if (this.$parent) curTab = this.$parent.find('[data-toggle=collapse][href="#' + this.$element.attr('id') + '"]') 293 294 collToggle.apply(this, arguments) 295 296 if ($.support.transition) { 297 this.$element.one($.support.transition.end, function(){ 298 299 prevTab.attr({ 'aria-selected':'false','aria-expanded':'false', 'tabIndex':'-1' }) 300 $prevPanel.attr({ 'aria-hidden' : 'true','tabIndex' : '-1'}) 301 302 curTab.attr({ 'aria-selected':'true','aria-expanded':'true', 'tabIndex':'0' }) 303 304 if($curPanel.hasClass('in')){ 305 $curPanel.attr({ 'aria-hidden' : 'false','tabIndex' : '0' }) 306 }else{ 307 curTab.attr({ 'aria-selected':'false','aria-expanded':'false'}) 308 $curPanel.attr({ 'aria-hidden' : 'true','tabIndex' : '-1' }) 309 } 310 }) 311 } 312 }else{ 313 collToggle.apply(this, arguments) 314 } 315 } 316 317 $.fn.collapse.Constructor.prototype.keydown = function (e) { 318 var $this = $(this) 319 , $items 320 , $tablist = $this.closest('div[role=tablist] ') 321 , index 322 , k = e.which || e.keyCode 323
324 $this = $(this) 325 if (!/(32|37|38|39|40)/.test(k)) return 326 if(k==32) $this.click() 327 328 $items = $tablist.find('[role=tab]') 329 index = $items.index($items.filter(':focus')) 330 331 if (k == 38 || k == 37) index-- // up & left 332 if (k == 39 || k == 40) index++ // down & right 333 if(index < 0) index = $items.length -1 334 if(index == $items.length) index = 0 335 336 $items.eq(index).focus() 337 338 e.preventDefault() 339 e.stopPropagation() 340 341 } 342 343 $(document).on('keydown.collapse.data-api','[data-toggle="collapse"]' , $.fn.collapse.Constructor.prototype.keydown) 344 345// Carousel Extension 346 // =============================== 347 348 $('.carousel').each(function (index) { 349 350 // This function positions a highlight box around the tabs in the tablist to use in focus styling 351 352 function setTablistHighlightBox() { 353 354 var $tab 355 , offset 356 , height 357 , width 358 , highlightBox = {} 359 360 highlightBox.top = 0 361 highlightBox.left = 32000 362 highlightBox.height = 0 363 highlightBox.width = 0 364 365 for (var i = 0; i < $tabs.length; i++) { 366 $tab = $tabs[i] 367 offset = $($tab).offset() 368 height = $($tab).height() 369 width = $($tab).width() 370 371// console.log(" Top: " + offset.top + " Left: " + offset.left + " Height: " + height + " Width: " + width) 372 373 if (highlightBox.top < offset.top) { 374 highlightBox.top = Math.round(offset.top) 375 } 376 377 if (highlightBox.height < height) { 378 highlightBox.height = Math.round(height) 379 } 380 381 if (highlightBox.left > offset.left) { 382 highlightBox.left = Math.round(offset.left) 383 } 384 385 var w = (offset.left - highlightBox.left) + Math.round(width) 386 387 if (highlightBox.width < w) { 388 highlightBox.width = w 389 } 390 391 } // end for 392 393// console.log("[HIGHLIGHT] Top: " + highlightBox.top + " Left: " + highlightBox.left + " Height: " + highlightBox.height + " Width: " + highlightBox.width) 394 395 $tablistHighlight.style.top = (highlightBox.top - 2) + 'px' 396 $tablistHighlight.style.left = (highlightBox.left - 2) + 'px' 397 $tablistHighlight.style.height = (highlightBox.height + 7) + 'px' 398 $tablistHighlight.style.width = (highlightBox.width + 8) + 'px' 399 400 } // end function 401 402 var $this = $(this) 403 , $prev = $this.find('[data-slide="prev"]') 404 , $next = $this.find('[data-slide="next"]') 405 , $tablist = $this.find('.carousel-indicators') 406 , $tabs = $this.find('.carousel-indicators li') 407 , $tabpanels = $this.find('.item') 408 , $tabpanel 409 , $tablistHighlight 410 , $pauseCarousel 411 , $complementaryLandmark 412 , $tab 413 , $is_paused = false 414 , offset 415 , height 416 , width 417 , i 418 , id_title = 'id_title' 419 , id_desc = 'id_desc' 420 421 422 $tablist.attr('role', 'tablist') 423 424 $tabs.focus(function() { 425 $this.carousel('pause') 426 $is_paused = true 427 $pauseCarousel.innerHTML = '<span class="sr-only">Play Carousel</span>'; 428 $pauseCarousel.className = "carousel-pause-button fa fa-play" 429 $(this).parent().addClass('active'); 430// $(this).addClass('focus') 431 setTablistHighlightBox() 432 $($tablistHighlight).addClass('focus') 433 $(this).parents('.carousel').addClass('contrast') 434 }) 435 436 $tabs.blur(function(event) { 437 $(this).parent().removeClass('active'); 438// $(this).removeClass('focus') 439 $($tablistHighlight).removeClass('focus') 440 $(this).parents('.carousel').removeClass('contrast') 441 }) 442 443 444 for (i = 0; i < $tabpanels.length; i++) { 445 $tabpanel = $tabpanels[i] 446 $tabpanel.setAttribute('role', 'tabpanel') 447 $tabpanel.setAttribute('id', 'tabpanel-' + index + '-' + i) 448 if($('#tab-' + index + '-' + i).length > 0) { 449 $tabpanel.setAttribute('aria-labelledby', 'tab-' + index + '-' + i) 450 } 451 } 452 453 if (typeof $this.attr('role') !== 'string') { 454 $this.attr('role', 'complementary'); 455 $this.attr('aria-labelledby', id_title); 456 $this.attr('aria-describedby', id_desc); 457 $this.prepend('<p id="' + id_desc + '" class="sr-only">A carousel is a rotating set of images, rotation stops on keyboard focus on carousel tab controls or hovering the mouse pointer over images. Use the tabs or the previous and next buttons to change the displayed slide.</p>') 458 $this.prepend('<h2 id="' + id_title + '" class="sr-only">Carousel content with ' + $tabpanels.length + ' slides.</h2>') 459 } 460 461 462 for (i = 0; i < $tabs.length; i++) { 463 $tab = $tabs[i] 464 465 $tab.setAttribute('role', 'tab') 466 $tab.setAttribute('id', 'tab-' + index + '-' + i) 467 $tab.setAttribute('aria-controls', 'tabpanel-' + index + '-' + i) 468 469 var tpId = '#tabpanel-' + index + '-' + i 470 var tabId = 'tab-' + index + '-' + i; 471 var caption = $this.find(tpId).find('h1').text() 472 var captionEle = $this.find(tpId).find('h1') 473 474 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = $this.find(tpId).find('h2').text(); captionEle = $this.find(tpId).find('h2') } 475 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = $this.find(tpId).find('h3').text(); captionEle = $this.find(tpId).find('h3') } 476 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = $this.find(tpId).find('h4').text(); captionEle = $this.find(tpId).find('h4') } 477 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = $this.find(tpId).find('h5').text(); captionEle = $this.find(tpId).find('h5') } 478 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = $this.find(tpId).find('h6').text(); captionEle = $this.find(tpId).find('h6') } 479 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = $this.find(tpId).text(); captionEle = $this.find(tpId) } 480 if ((typeof caption !== 'string') || (caption.length === 0)) { caption = "no title"; captionEle = null; } 481 482// console.log("CAPTION: " + caption ) 483 484 if(typeof captionEle !== 'undefined' && captionEle !== null) { 485 //captionEle.attr('id', tabId); 486 } 487 488 var tabName = document.createElement('span') 489 tabName.setAttribute('class', 'sr-only') 490 tabName.innerHTML='Slide ' + (i+1) 491 if (caption) tabName.innerHTML += ": " + caption 492 $tab.appendChild(tabName) 493 494 } 495 496 // create div for focus styling of tablist 497 $tablistHighlight = document.createElement('div') 498 $tablistHighlight.className = 'carousel-tablist-highlight' 499 document.body.appendChild($tablistHighlight) 500 501 // if we have tabs to play or pause 502 if($tabs.length > 0) { 503 // create button for screen reader users to stop rotation of carousel
504 505 // create button for screen reader users to pause carousel for virtual mode review 506 $complementaryLandmark = document.createElement('aside') 507 $complementaryLandmark.className = 'carousel-pause-wrapper' 508 $complementaryLandmark.setAttribute('aria-label', 'carousel pause/play control') 509 $this.prepend($complementaryLandmark) 510 511 $pauseCarousel = document.createElement('button') 512 $pauseCarousel.className = "carousel-pause-button fa fa-pause" 513 $pauseCarousel.innerHTML = '<span class="sr-only">Pause Carousel</span>' 514 $pauseCarousel.setAttribute('title', "Pause/Play carousel button can be used by screen reader users to stop carousel animations") 515 $($complementaryLandmark).append($pauseCarousel) 516 517 $($pauseCarousel).click(function() { 518 if ($is_paused) { 519 $pauseCarousel.innerHTML = '<span class="sr-only">Pause Carousel</span>' 520 $pauseCarousel.className = "carousel-pause-button fa fa-pause" 521 $this.carousel('cycle') 522 $is_paused = false 523 } else { 524 $pauseCarousel.innerHTML = '<span class="sr-only">Play Carousel</span>' 525 $pauseCarousel.className = "carousel-pause-button fa fa-play" 526 $this.carousel('pause') 527 $is_paused = true 528 } 529 }) 530 $($pauseCarousel).focus(function() { 531 $(this).addClass('focus') 532 }) 533 534 $($pauseCarousel).blur(function() { 535 $(this).removeClass('focus') 536 }) 537 } 538 setTablistHighlightBox() 539 540 $( window ).resize(function() { 541 setTablistHighlightBox() 542 }) 543 544 // Add space bar behavior to prev and next buttons for SR compatibility 545 $prev.attr('aria-label', 'Previous Slide') 546 $prev.keydown(function(e) { 547 var k = e.which || e.keyCode 548 if (/(13|32)/.test(k)) { 549 e.preventDefault() 550 e.stopPropagation() 551 $prev.trigger('click'); 552 } 553 }); 554 555 $prev.focus(function() { 556 $(this).parents('.carousel').addClass('contrast') 557 }) 558 559 $prev.blur(function() { 560 $(this).parents('.carousel').removeClass('contrast') 561 }) 562 563 $next.attr('aria-label', 'Next Slide') 564 $next.keydown(function(e) { 565 var k = e.which || e.keyCode 566 if (/(13|32)/.test(k)) { 567 e.preventDefault() 568 e.stopPropagation() 569 $next.trigger('click'); 570 } 571 }); 572 573 $next.focus(function() { 574 $(this).parents('.carousel').addClass('contrast') 575 }) 576 577 $next.blur(function() { 578 $(this).parents('.carousel').removeClass('contrast') 579 }) 580 581 $('.carousel-inner a').focus(function() { 582 $(this).parents('.carousel').addClass('contrast') 583 }) 584 585 $('.carousel-inner a').blur(function() { 586 $(this).parents('.carousel').removeClass('contrast') 587 }) 588 589 $tabs.each(function () { 590 var item = $(this) 591 if(item.hasClass('active')) { 592 item.attr({ 'aria-selected': 'true', 'tabindex' : '0' }) 593 }else{ 594 item.attr({ 'aria-selected': 'false', 'tabindex' : '-1' }) 595 } 596 }) 597 }) 598 599 var slideCarousel = $.fn.carousel.Constructor.prototype.slide 600 $.fn.carousel.Constructor.prototype.slide = function (type, next) { 601 var $element = this.$element 602 , $active = $element.find('[role=tabpanel].active') 603 , $next = next || $active[type]() 604 , $tab 605 , $tab_count = $element.find('[role=tabpanel]').length 606 , $prev_side = $element.find('[data-slide="prev"]') 607 , $next_side = $element.find('[data-slide="next"]') 608 , $index = 0 609 , $prev_index = $tab_count -1 610 , $next_index = 1 611 , $id 612 613 if ($next && $next.attr('id')) { 614 $id = $next.attr('id') 615 $index = $id.lastIndexOf("-") 616 if ($index >= 0) $index = parseInt($id.substring($index+1), 10) 617 618 $prev_index = $index - 1 619 if ($prev_index < 1) $prev_index = $tab_count - 1 620 621 $next_index = $index + 1 622 if ($next_index >= $tab_count) $next_index = 0 623 } 624 625 $prev_side.attr('aria-label', 'Show slide ' + ($prev_index+1) + ' of ' + $tab_count) 626 $next_side.attr('aria-label', 'Show slide ' + ($next_index+1) + ' of ' + $tab_count) 627 628 629 slideCarousel.apply(this, arguments) 630 631 $active 632 .one('bsTransitionEnd', function () { 633 var $tab 634 635 $tab = $element.find('li[aria-controls="' + $active.attr('id') + '"]') 636 if ($tab) $tab.attr({'aria-selected':false, 'tabIndex': '-1'}) 637 638 $tab = $element.find('li[aria-controls="' + $next.attr('id') + '"]') 639 if ($tab) $tab.attr({'aria-selected': true, 'tabIndex': '0'}) 640 641 }) 642 } 643 644 var $this; 645 $.fn.carousel.Constructor.prototype.keydown = function (e) { 646
647 $this = $this || $(this) 648 if(this instanceof Node) $this = $(this) 649 650 function selectTab(index) { 651 if (index >= $tabs.length) return 652 if (index < 0) return 653 654 $carousel.carousel(index) 655 setTimeout(function () { 656 $tabs[index].focus() 657 // $this.prev().focus() 658 }, 150) 659 } 660 661 var $carousel = $(e.target).closest('.carousel') 662 , $tabs = $carousel.find('[role=tab]') 663 , k = e.which || e.keyCode 664 , index 665 666 if (!/(37|38|39|40)/.test(k)) return 667 668 index = $tabs.index($tabs.filter('.active')) 669 if (k == 37 || k == 38) { // Up 670 index-- 671 selectTab(index); 672 } 673 674 if (k == 39 || k == 40) { // Down 675 index++ 676 selectTab(index); 677 } 678 679 e.preventDefault() 680 e.stopPropagation() 681 } 682 $(document).on('keydown.carousel.data-api', 'li[role=tab]', $.fn.carousel.Constructor.prototype.keydown) 683 684 685})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.