1svDocReady(function () { 2 const KEYS = { 3 ESCAPE: 27, 4 SPACE: 32, 5 }; 6 7 const TRANSITION_MIRROR_MAP = { 8 show: 'hide', 9 slideDown: 'slideUp', 10 slideUp: 'slideDown', 11 fade: 'fade', 12 slide: 'slide', 13 }; 14 15 iwButton = (function () { 16 const init = function ($context) { 17 $context 18 .find( 19 '[data-iw-button="open-button"], [data-iw-button="toggle-button"], [data-iw-button="close-button"]' 20 ) 21 .each(function () { 22 const $button = jQuery(this); 23 const duration = $button.attr( 24 'data-iw-button-transition-duration' 25 ); 26 27 const options = { 28 type: $button.attr('data-iw-button'), 29 controlsId: $button.attr('data-iw-button-controls-id'), 30 pressedClass: $button.attr('data-iw-button-pressed-class'), 31 expanded: $button.attr('data-iw-button-expanded') === 'true', 32 clickOnInit: 33 $button.attr('data-iw-button-click-on-init') === 'true', 34 transition: $button.attr( 35 'data-iw-button-controls-transition' 36 ), 37 groupName: $button.attr('data-iw-button-group'), 38 groupSkipTransition: $button.attr( 39 'data-iw-button-group-skip-transition' 40 ), 41 transitionClose: $button.attr( 42 'data-iw-button-controls-transition-close' 43 ), 44 transitionDuration: 45 typeof duration !== 'undefined' 46 ? parseInt(duration) 47 : duration, 48 focusTargeting: $button.attr( 49 'data-iw-button-focus-targeting' 50 ), 51 focusFirstLinkInTarget: $button.attr( 52 'data-iw-button-focus-first-link-in-target' 53 ), 54 scrollTarget: $button.attr('data-iw-button-scroll-target'), 55 blur: $button.attr('data-iw-button-blur') === 'true', 56 blurClass: $button.attr('data-iw-button-blur-class'), 57 modal: $button.attr('data-iw-button-modal') === 'true', 58 closeOnClickOutside: $button.attr( 59 'data-iw-button-close-on-click-outside' 60 ), 61 text: $button.attr('data-iw-button-text'), 62 pressedText: $button.attr('data-iw-button-pressed-text'), 63 }; 64 $button.iwButton(options); 65 }); 66 }; 67 68 return { 69 init: init, 70 }; 71 })(); 72 73 const methods = { 74 init: function (options) { 75 const $thisButton = jQuery(this); 76 77 $thisButton.attr('role', 'button'); 78 if (!$thisButton[0].hasAttribute('href')) { 79 $thisButton.attr('href', '#'); 80 } 81 82 if ( 83 typeof options.transitionClose === 'undefined' && 84 typeof options.transition !== 'undefined' 85 ) { 86 options.transitionClose = TRANSITION_MIRROR_MAP[options.transition]; 87 } 88 89 const oldOptions = iwGetOptions($thisButton); 90 if (typeof oldOptions !== 'undefined') { 91 // Button already initialized, just update the options 92 const newOptions = jQuery.extend({}, oldOptions, options); 93 $thisButton.data('iw-button-options', newOptions); 94 return; 95 } else { 96 options = jQuery.extend({}, jQuery.fn.iwButton.defaults, options); 97 $thisButton.data('iw-button-options', options); 98 } 99 100 if (options.groupName) { 101 $thisButton.attr('data-iw-button-group', options.groupName); 102 } 103 104 if (options.controlsId && options.controlsId.length > 0) { 105 $thisButton.attr('aria-controls', options.controlsId); 106 } 107 108 $thisButton.attr('aria-label', options.text); 109 110 switch (options.type) { 111 case 'open-button': 112 case 'toggle-button': 113 if (!hasAttr($thisButton, 'aria-expanded')) { 114 $thisButton.attr('aria-expanded', 'false'); 115 } 116 break; 117 case 'close-button': 118 if (!hasAttr($thisButton, 'aria-expanded')) { 119 $thisButton.attr('aria-expanded', 'true'); 120 } 121 break; 122 default: 123 } 124 125 if (options.expanded) { 126 iwOpen( 127 $thisButton, 128 jQuery.extend({}, options, { transition: 'show' }) 129 ); 130 } 131 132 // Bind events 133 $thisButton.on('click keypress', function (e) { 134 if (e.type === 'click' || e.which == KEYS.SPACE) { 135 e.preventDefault();
136 let $this = jQuery(this); 137 let clickOptions = iwGetOptions($this); 138 139 switch (clickOptions.type) { 140 case 'toggle-button': 141 iwToggle($this, clickOptions); 142 break; 143 case 'open-button': 144 iwOpen($this, clickOptions); 145 break; 146 case 'close-button': 147 iwClose($this, clickOptions); 148 break; 149 default: 150 } 151 } 152 }); 153 if (options.clickOnInit) { 154 $thisButton.trigger('click'); 155 } 156 }, 157 open: iwOpen, 158 close: iwClose, 159 toggle: iwToggle, 160 }; 161 162 function iwToggle($toggleButton, options) { 163 if ($toggleButton.attr('aria-expanded') === 'false') { 164 iwOpen($toggleButton, options); 165 } else { 166 iwClose($toggleButton, options); 167 } 168 } 169 170 function iwOpen($openButton, options) { 171 const iwOpenTargetId = options.controlsId; 172 const $iwOpenTarget = jQuery('#' + iwOpenTargetId); 173 let transition = options.transition; 174 175 $openButton.addClass(options.pressedClass); 176 177 if (options.blur) { 178 const $blurElement = jQuery('.iw-blur'); 179 $blurElement.trigger('click'); 180 $blurElement.attr('class', 'iw-blur'); 181 if (options.blurClass) { 182 $blurElement.addClass(options.blurClass); 183 } 184 185 if (options.modal) { 186 jQuery('body').addClass('iw-modal-open'); 187 } 188 189 if (options.transition === 'show') { 190 $blurElement.show(); 191 } else { 192 $blurElement.fadeIn(options.transitionDuration); 193 } 194 const blurOptions = iwGetOptions($blurElement); 195 blurOptions.controlsId = iwOpenTargetId; 196 blurOptions.transitionClose = options.transitionClose; 197 $blurElement.attr('data-iw-button-controls-id', iwOpenTargetId); 198 $blurElement.attr('aria-controls', iwOpenTargetId); 199 $blurElement.attr('aria-expanded', 'true'); 200 } 201 202 if (options.groupName) { 203 const $groupButtons = jQuery( 204 '[data-iw-button-group="' + 205 options.groupName + 206 '"][aria-expanded="true"]' 207 ).not($openButton); 208 209 if ($groupButtons.length > 0) { 210 const closeOptions = {}; 211 if (options.groupSkipTransition) { 212 transition = 'show'; 213 closeOptions.transitionClose = 'hide'; 214 } 215 216 $groupButtons.iwButton('close', closeOptions); 217 } 218 } 219 220 options.beforeStateChange({ options: options, open: true }); 221 222 let transitionOpenComplete = function () { 223 options.afterTransition({ open: true }); 224 }; 225 226 switch (transition) { 227 case 'show': 228 $iwOpenTarget.show({ complete: transitionOpenComplete }); 229 break; 230 case 'fade': 231 $iwOpenTarget.fadeIn( 232 options.transitionDuration, 233 transitionOpenComplete 234 ); 235 break; 236 case 'slideDown': 237 $iwOpenTarget.slideDown( 238 options.transitionDuration, 239 transitionOpenComplete 240 ); 241 break; 242 case 'slideUp': 243 $iwOpenTarget.slideUp( 244 options.transitionDuration, 245 transitionOpenComplete 246 ); 247 break; 248 case 'slide': 249 $iwOpenTarget.animate( 250 { 251 width: ['show', 'swing'], 252 }, 253 options.transitionDuration, 254 'linear', 255 transitionOpenComplete 256 ); 257 break; 258 default: 259 $iwOpenTarget.show({ complete: transitionOpenComplete }); 260 } 261 262 if (options.focusTargeting) { 263 let selector = 264 '[data-iw-button-focus-target=' + options.focusTargeting + ']'; 265 selector += options.focusFirstLinkInTarget === 'true' ? ' a' : ''; 266 const $focusTarget = 267 options.focusFirstLinkInTarget === 'true' 268 ? jQuery(selector).first() 269 : jQuery(selector); 270 $focusTarget 271 .attr('tabindex', '-1') 272 .trigger('focus') 273 .attr('tabindex', '0'); 274 } 275 276 if (options.scrollTarget) { 277 const $scrollingTarget = jQuery('#' + options.scrollTarget); 278 window.scrollTo(0, $scrollingTarget.offset().top); 279 } 280 stateChange(options, true, $openButton); 281 } 282 283 function iwClose($closeButton, options) { 284 const iwCloseTargetId = options.controlsId; 285 const $iwCloseTarget = jQuery('#' + iwCloseTargetId); 286 $closeButton.removeClass(options.pressedClass); 287 const blurControls = jQuery('.iw-blur').attr( 288 'data-iw-button-controls-id' 289 ); 290 if (blurControls === options.controlsId) { 291 const $blurElement = jQuery('.iw-blur'); 292 if (options.transitionClose === 'show') { 293 $blurElement.hide(); 294 } else { 295 $blurElement.fadeOut(options.transitionDuration); 296 } 297 $blurElement.attr('aria-expanded', 'false'); 298 } 299 300 options.beforeStateChange({ options: options, open: false }); 301 302 let transitionCloseComplete = function () { 303 options.afterTransition({ open: false }); 304 }; 305 306 switch (options.transitionClose) { 307 case 'hide': 308 $iwCloseTarget.hide({ complete: transitionCloseComplete }); 309 break; 310 case 'fade': 311 $iwCloseTarget.fadeOut( 312 options.transitionDuration, 313 transitionCloseComplete 314 ); 315 break; 316 case 'slideDown': 317 $iwCloseTarget.slideDown( 318 options.transitionDuration, 319 transitionCloseComplete 320 ); 321 break; 322 case 'slideUp': 323 $iwCloseTarget.slideUp( 324 options.transitionDuration, 325 transitionCloseComplete 326 ); 327 break; 328 case 'slide': 329 $iwCloseTarget.animate( 330 { 331 width: ['hide', 'swing'], 332 }, 333 options.transitionDuration, 334 'linear', 335 transitionCloseComplete 336 ); 337 break; 338 default: 339 $iwCloseTarget.hide({ complete: transitionCloseComplete }); 340 } 341 jQuery('body').removeClass('iw-modal-open'); 342 343 if (options.focusTargeting) { 344 let selector = 345 '[data-iw-button-focus-target=' + options.focusTargeting + ']'; 346 selector += options.focusFirstLinkInTarget === 'true' ? ' a' : ''; 347 const $focusTarget = 348 options.focusFirstLinkInTarget === 'true' 349 ? jQuery(selector).first() 350 : jQuery(selector); 351 $focusTarget 352 .attr('tabindex', '-1') 353 .trigger('focus') 354 .attr('tabindex', '0'); 355 } 356 357 stateChange(options, false, $closeButton); 358 } 359 360 function stateChange(options, open, $pressedButton) { 361 if (options.afterStateChange) { 362 options.afterStateChange({ 363 options: options, 364 open: open, 365 $pressedButton: $pressedButton, 366 }); 367 } 368 369 jQuery('[aria-controls=' + options.controlsId + ']').each(function () { 370 const $button = jQuery(this); 371 const buttonOptions = $button.data('iw-button-options'); 372 if (buttonOptions) { 373 if (buttonOptions.pressedClass) { 374 $button.toggleClass(buttonOptions.pressedClass, open); 375 } 376 377 if (options.text && options.pressedText) { 378 const replaceFrom = open ? options.text : options.pressedText; 379 const $textContainer = $button.find( 380 ':contains("' + replaceFrom + '")' 381 ); 382 383 if ($textContainer.length > 0) { 384 const replaceTo = open ? options.pressedText : options.text; 385 $textContainer 386 .contents() 387 .filter(function () { 388 return this.nodeType == Node.TEXT_NODE; 389 }) 390 .each(function () { 391 this.textContent = this.textContent.replace( 392 replaceFrom, 393 replaceTo 394 ); 395 }); 396 } 397 } 398 } 399 400 $button.attr('aria-expanded', open); 401 options.expanded = open; 402 $button.attr('aria-label', open ? options.pressedText : options.text); 403 404 if (options.closeOnClickOutside) { 405 const clickHandler = (e) => { 406 const clickOutside = !$svjq(e.target).closest( 407 `#${options.controlsId}` 408 ).length && !$svjq(e.target).closest(`[data-iw-button-controls-id="${options.controlsId}"]`).length; 409 if (options.expanded && clickOutside) { 410 iwClose($button, options); 411 } 412 }; 413 414 setTimeout(() => $svjq(document).one('click', clickHandler), 0); 415 } 416 }); 417 418 if (options.modal) { 419 const $controlContainer = jQuery('#' + options.controlsId); 420 let $focustrap = $controlContainer.closest('.iw-focustrap'); 421 if ($focustrap.length === 0) { 422 $focustrap = $controlContainer; 423 } 424 425 $focustrap.each(function () { 426 const $container = jQuery(this); 427 if (open) { 428 const $loopToEnd = jQuery( 429 '<a href="#" class="iw-focustrap-link sv-visuallyhidden"></a>' 430 ); 431 const $loopToStart = $loopToEnd.clone(); 432 433 $container.prepend($loopToEnd); 434 $container.append($loopToStart); 435 436 $loopToEnd.on('focus', function () { 437 getTabbableElements($container).last().trigger('focus'); 438 }); 439 440 $loopToStart.on('focus', function () { 441 getTabbableElements($container).first().trigger('focus'); 442 }); 443 } 444 }); 445 446 if (!open) { 447 jQuery('.iw-focustrap-link').remove(); 448 449 if (options.type === 'close-button') { 450 const focusTargetId = options.controlsId; 451 jQuery( 452 '[data-iw-button-controls-id=' + focusTargetId + ']:visible' 453 ).trigger('focus'); 454 } 455 } 456 //eslint-disable-next-line no-inner-declarations 457 function getTabbableElements($container) { 458 return $container.find(':tabbable:not(.iw-focustrap-link)'); 459 } 460 } 461 } 462 463 function hasAttr($element, attributeName) { 464 return typeof $element.attr(attributeName) !== 'undefined'; 465 } 466 467 function iwGetOptions($button) { 468 return $button.data('iw-button-options'); 469 } 470 471 jQuery.fn.iwButton = function (methodOrOptions, methodArguments) { 472 const objs = this; 473 for (let i = 0; i < objs.length; i++) { 474 let obj = objs[i]; 475 if (methods[methodOrOptions]) { 476 const method = methods[methodOrOptions]; 477 let options = iwGetOptions(jQuery(this)); 478 if (options) { 479 if (methodArguments) { 480 options = jQuery.extend({}, options, methodArguments); 481 } 482 method.apply(obj, [jQuery(obj), options]); 483 } else { 484 console.error('iwButton not initialized for object', this); 485 } 486 } else if (typeof methodOrOptions === 'object' || !methodOrOptions) { 487 // Default to 'init' 488 methods.init.apply(obj, arguments); 489 } else { 490 console.error( 491 'Method ' + 492 methodOrOptions + 493 ' does not exist on jQuery.iwButton', 494 obj 495 ); 496 } 497 } 498 return this; 499 }; 500 501 jQuery.fn.iwButton.defaults = { 502 transition: 'show', 503 transitionClose: 'hide', 504 transitionDuration: 100, 505 afterTransition: function (state) { }, 506 beforeStateChange: function (state) { }, 507 afterStateChange: function (state) { 508 this.onStateChange(state); 509 }, 510 onStateChange: function (state) { }, 511 }; 512 513 // Init iwButtons by attributes 514
515 const $blurElement = jQuery( 516 '<div class="iw-blur" aria-expanded="true" data-iw-button="close-button" role="button" data-iw-button-modal="true" aria-label="Stäng popup"></div>' 517 ); 518 jQuery('body').append($blurElement); 519 jQuery(document).on('keyup', function (e) { 520 if (e.which === KEYS.ESCAPE) { 521 $blurElement.iwButton('close'); 522 } 523 }); 524 525 iwButton.init(jQuery(document)); 526});
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.