PageSourceSearch

https://www.val.se/download/18.162047b519a91d05331137ed/1730207437593/iw-button.js

js val.se collected 2026-10-02 03:27:46 UTC 17,669 bytes, 526 lines download raw bytes

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.