1/** 2 * Owl Carousel v2 Accessibility Plugin 3 * Version 0.2.2 4 * © Geoffrey Roberts 2016 5 * additions by Ambientia 2023 6 */ 7 8;(function($, window, document){ 9 var Owl2A11y = function(carousel) { 10 this._core = carousel; 11 this._initialized = false; 12 13 this._core._options = $.extend(Owl2A11y.defaults, this._core.options); 14 15 this.$element = this._core.$element; 16 17 var setCurrent = $.proxy(function(e) { 18 this.setCurrent(e); 19 }, this); 20 21 this._handlers = { 22 'initialized.owl.carousel': $.proxy(function(e) { 23 this.setupRoot(); 24 if (e.namespace && !this._initialized) { 25 this.setupFocus(); 26 this.setupKeyboard(); 27 } 28 this.setCurrent(e); 29 }, this), 30 'changed.owl.carousel': setCurrent, 31 'translated.owl.carousel': setCurrent, 32 'refreshed.owl.carousel': setCurrent, 33 'resized.owl.carousel': setCurrent 34 }; 35 this.$element.on(this._handlers); 36 }; 37 38 39 /* PREFERENCES */ 40 41 /** 42 * Contains default parameters, if there were any. 43 */ 44 Owl2A11y.defaults = {}; 45 46 47 /* EVENT HANDLERS */ 48 49 /** 50 * Adds support for things that don't map nicely to the root object 51 * such as event handlers. 52 */ 53 Owl2A11y.eventHandlers = {}; 54 55 /** 56 * Get a callback for keyup events within this carousel. 57 * 58 * @return callback 59 * An event callback that takes an Event as an argument. 60 */ 61 Owl2A11y.prototype.getDocumentKeyUp = function(){ 62 var self = this; 63 return function(e) { 64 var eventTarg = $(e.target), 65 targ = self.focused(eventTarg), 66 action = null; 67 68 if (!!targ) { 69 if (e.keyCode == 37 || e.keyCode == 38) { 70 action = 'prev.owl.carousel'; 71 } 72 else if (e.keyCode == 39 || e.keyCode == 40) { 73 action = 'next.owl.carousel'; 74 } 75 else if (e.keyCode == 13) { 76 if (eventTarg.hasClass('owl-prev')) action = 'prev.owl.carousel'; 77 else if (eventTarg.hasClass('owl-next')) action = 'next.owl.carousel'; 78 else if (eventTarg.hasClass('owl-dot')) action = 'click'; 79 } 80 81 if (!!action) targ.trigger(action); 82 } 83 }; 84 }; 85 86 87 /* SETUP AND TEAR DOWN */ 88 89 /** 90 * Assign attributes to the root element. 91 */ 92 Owl2A11y.prototype.setupRoot = function() { 93 this._core.$stage.attr({ 94 role: "region", 95 "aria-live": "off", 96 "aria-roledescription": setAriaLabel(document.documentElement.lang, "carousel"), 97 "aria-label": setAriaLabel(document.documentElement.lang, "ariaLabel") 98 }); 99 var total_children = this._core.$stage.children().length; 100 this._core.$stage.children().each(function(idx) { 101 var item = $(this); 102 item.attr({ 103 role: 'group', 104 "aria-roledescription" : setAriaLabel(document.documentElement.lang, "slide"), 105 "aria-label" : `${idx + 1} / ${total_children}` 106 }); 107 }); 108 }; 109 110 /** 111 * Setup keyboard events for this carousel. 112 */ 113 Owl2A11y.prototype.setupKeyboard = function(){ 114 // Only needed to initialise once for the entire document 115 if (!this.$element.attr('data-owl-access-keyup')) { 116 this.$element.bind('keyup', this.getDocumentKeyUp()) 117 .attr('data-owl-access-keyup', '1'); 118 } 119 this.$element.attr('data-owl-carousel-focusable', '1'); 120 }; 121 122 /** 123 * Setup focusing behaviour for the carousel. 124 */ 125 Owl2A11y.prototype.setupFocus = function(){ 126 // Only needed to initialise once for the entire document 127 this.$element.bind('focusin', function(){ 128 $(this).attr({ 129 'data-owl-carousel-focused': '1', 130 'aria-live': 'polite' 131 }).trigger('stop.owl.autoplay'); 132 }).bind('focusout', function(){ 133 $(this).attr({ 134 'data-owl-carousel-focused': '0', 135 'aria-live': 'off' 136 }).trigger('play.owl.autoplay'); 137 }); 138 139 // Add tabindex to allow navigation to be focused. 140 if (!!this._core._plugins.navigation) { 141 var navPlugin = this._core._plugins.navigation, 142 toFocus = [];
143 if (!!navPlugin._controls.$previous) { 144 toFocus.push(navPlugin._controls.$previous); 145 } 146 if (!!navPlugin._controls.$next) { 147 toFocus.push(navPlugin._controls.$next); 148 } 149 if (!!navPlugin._controls.$indicators) { 150 toFocus.push(navPlugin._controls.$indicators.children()); 151 } 152 $.each(toFocus, function(){ 153 this.attr('tabindex', '0'); 154 }); 155 } 156 }; 157 158 /** 159 * Assign attributes to the root element. 160 */ 161 Owl2A11y.prototype.destroy = function() { 162 this.$element.unbind('keyup', this.eventHandlers.documentKeyUp) 163 .removeAttr('data-owl-access-keyup data-owl-carousel-focusable') 164 .unbind('focusin focusout'); 165 }; 166 167 168 /* HELPER FUNCTIONS */ 169 170 /** 171 * Identifies all focusable elements within a given element. 172 * 173 * @param DOMElement elem 174 * A DOM element. 175 * 176 * @return jQuery 177 * A jQuery object that may refer to zero or more focusable elements. 178 */ 179 Owl2A11y.prototype.focusableElems = function(elem) { 180 return $(elem).find('a, input, select, button, *[tabindex]'); 181 }; 182 183 /** 184 * Identifies all focusable elements within a given element. 185 * 186 * @param jQeury elems 187 * A jQuery object that may refer to zero or more focusable elements. 188 * @param boolean enable 189 * Whether focus is to be enabled on these elements or not. 190 */ 191 Owl2A11y.prototype.adjustFocus = function(elems, enable){ 192 elems.each(function(){ 193 var item = $(this); 194 var newTabIndex = '0', 195 storeTabIndex = '0'; 196 197 currentTabIndex = item.attr('tabindex'), 198 storedTabIndex = item.attr('data-owl-temp-tabindex'); 199 200 if (enable) { 201 newTabIndex = ( 202 typeof(storedTabIndex) != 'undefined' && (storedTabIndex != '-1') ? 203 item.attr('data-owl-temp-tabindex') : 204 '0' 205 ); 206 storedTabIndex = newTabIndex; 207 } 208 else { 209 newTabIndex = '-1'; 210 storedTabIndex = ( 211 (typeof(currentTabIndex) != 'undefined') || (currentTabIndex != '-1') ? 212 currentTabIndex : 213 '0' 214 ); 215 } 216 217 item.attr({ 218 tabindex: newTabIndex, 219 'data-owl-temp-tabindex': storeTabIndex 220 }); 221 }); 222 }; 223 224 /** 225 * Get the root element if we are focused within it. 226 * 227 * @param DOMElement targ 228 * An element that might be within this carousel. 229 * 230 * @return mixed 231 * Either the jQuery element containing the root element, or NULL. 232 */ 233 Owl2A11y.prototype.focused = function(targ){ 234 var targ = $(targ); 235 if (targ.attr('data-owl-carousel-focused') == 1) { 236 return targ; 237 } 238 var closest = targ.closest('[data-owl-carousel-focused="1"]'); 239 if (closest.length > 0) return closest; 240 return null; 241 }; 242 243 244 /* UPDATE FUNCTIONS */ 245 246 /** 247 * Identify active elements, set WAI-ARIA sttributes accordingly, 248 * scroll to show element if we need to, and set up focusing. 249 * 250 * @param Event e 251 * The triggering event. 252 */ 253 Owl2A11y.prototype.setCurrent = function(e) { 254 var targ = this.focused($(':focus')), 255 element = this._core.$element, 256 stage = this._core.$stage, 257 focusableElems = this.focusableElems, 258 adjustFocus = this.adjustFocus; 259 260 if (!!stage) { 261 var offs = stage.offset(); 262 /* 263 if (!!targ) { 264 window.scrollTo( 265 offs.left, 266 offs.top - parseInt($('body').css('padding-top'), 10) 267 ); 268 } 269 */ 270 this._core.$stage.children().each(function(i) { 271 var item = $(this); 272 var focusable = focusableElems(this); 273 274 275 // Use the active class to determine if we can see it or not. 276 // Pretty lazy, but the Owl API doesn't make it easy to tell 277 // from indices alone. 278 if (item.hasClass('active')) { 279 item.attr('aria-hidden', 'false'); 280 adjustFocus(focusable, true); 281 } 282 else { 283 item.attr('aria-hidden', 'true'); 284 adjustFocus(focusable, false); 285 } 286 }); 287 288 if (!!targ) { 289 // Focus on the root element after we're done moving, 290 // but only if we're not using the controls. 291 setTimeout(function(){ 292 var newFocus = element;
293 if ($(':focus').closest('.owl-controls').length) { 294 newFocus = $(':focus'); 295 } 296 newFocus.focus(); 297 }, 250); 298 } 299 } 300 }; 301 302 $.fn.owlCarousel.Constructor.Plugins['Owl2A11y'] = Owl2A11y; 303})(window.Zepto || window.jQuery, window, document); 304 305function setAriaLabel(langCode,str) { 306 let lang = "en"; 307 if (langCode) { 308 lang = langCode.split(langCode.indexOf('-') === -1 ? '_' : '-'); 309 } 310 const languages = ["fi","sv","en"]; 311 312 const titles = { 313 "fi": { 314 "carousel": "Karuselli", 315 "slide": "Ruutu", 316 "ariaLabel" : "Sisältökaruselli" 317 }, 318 "sv": { 319 "carousel": "karusell", 320 "slide": "slide", 321 "ariaLabel" : "Innehållskarusell" 322 }, 323 "en" : { 324 "carousel": "carousel", 325 "slide": "slide", 326 "ariaLabel" : "Content carousel" 327 } 328 } 329 return languages.some(l => l.includes(lang[0])) ? titles[lang[0]][str] : titles["fi"]["ariaLabel"]; 330 331}
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.