PageSourceSearch

https://museumsmile.lu/modules/contrib/ckeditor_accordion/js/accordion.frontend.js?tl6xk6

js museumsmile.lu collected 2026-10-02 16:19:49 UTC 10,170 bytes, 278 lines download raw bytes

1/**
2 * @file
3 * CKEditor Accordion functionality.
4 */
5
6(function (Drupal, drupalSettings) {
7  'use strict';
8
9  let animating = false;
10  let animateObjects = [];
11
12  // Default config.
13  const defaults = {
14    duration: 300,
15    easing: (t, b, c, d) => {
16      if ((t /= d / 2) < 1)
17        return c / 2 * t * t + b;
18      return -c / 2 * ((--t) * (t - 2) - 1) + b;
19    }
20  };
21
22  // Two directions.
23  const directions = {
24    OPEN: 1,
25    CLOSE: 2
26  };
27
28  // Animate fn.
29  const animate = (animateSets, now) => {
30    let animationContinue = false;
31    for(let x = 0; x < animateSets.length; x++) {
32      let element = animateSets[x][0];
33      let options = animateSets[x][1];
34      if (!options.startTime) {
35        options.startTime = now;
36      }
37      let currentTime = now - options.startTime;
38      animationContinue = currentTime < options.duration;
39      if (animationContinue) {
40        let newHeight = options.easing(currentTime, options.startingHeight, options.distanceHeight, options.duration);
41        element.style.height = `${newHeight.toFixed(2)}px`;
42      }
43    }
44
45    if (animationContinue) {
46      window.requestAnimationFrame((timestamp) => animate(animateSets, timestamp));
47    }
48    else {
49      for(let x = 0; x < animateSets.length; x++) {
50        let element = animateSets[x][0];
51        let options = animateSets[x][1];
52        if (options.direction === directions.CLOSE) {
53          element.style.display = 'none';
54        }
55        if (options.direction === directions.OPEN) {
56          element.style.display = 'block';
57        }
58
59        // Remove element animation styles.
60        element.style.height = null;
61        element.style.overflow = null;
62        element.style.marginTop = null;
63        element.style.marginBottom = null;
64        element.style.paddingTop = null;
65        element.style.paddingBottom = null;
66      }
67
68      // Reset vars.
69      animateObjects = [];
70      animating = false;
71    }
72  }
73
74  // Animation handler.
75  const doAnimations = (animateObjects) => {
76    let doAnimate = drupalSettings.ckeditorAccordion.accordionStyle.animateAccordionOpenAndClose ?? true;
77    if (!doAnimate) {
78      animateObjects.forEach(object => {
79        const element = object[1];
80        if (object[0] == 'slideUp') {
81          element.style.display = 'none';
82        }
83        else {
84          element.style.display = 'block';
85        }
86      });
87      animating = false;
88    }
89    else {
90      let animateSets = [];
91      animateObjects.forEach(object => {
92        const element = object[1];
93        let options = Object.assign({}, defaults);
94        if (object[0] == 'slideUp') {
95          options.direction = directions.CLOSE;
96          options.to = 0;
97          options.startingHeight = element.scrollHeight;
98          options.distanceHeight = -options.startingHeight;
99
100          // Set element animation styles.
101          element.style.display = 'block';
102          element.style.overflow = 'hidden';
103          element.style.marginTop = '0';
104          element.style.marginBottom = '0';
105          element.style.paddingTop = '0';
106          element.style.paddingBottom = '0';
107        } else {
108          element.style.height = '0px';
109
110          // Set element animation styles.
111          element.style.display = 'block';
112          element.style.overflow = 'hidden';
113          element.style.marginTop = '0';
114          element.style.marginBottom = '0';
115          element.style.paddingTop = '0';
116          element.style.paddingBottom = '0';
117
118          options.direction = directions.OPEN;
119          options.to = element.scrollHeight;
120          options.startingHeight = 0;
121          options.distanceHeight = options.to;
122        }
123
124        delete options.startTime;
125        animateSets.push([element, options]);
126      });
127
128      window.requestAnimationFrame((timestamp) => animate(animateSets, timestamp));
129    }
130  }
131
132  Drupal.behaviors.ckeditorAccordion = {
133
134    attach: function (context, settings) {
135      const $ckeditorAccordions = once('ckeditorAccordions', '.ckeditor-accordion', context);
136      const doAnimate = drupalSettings.ckeditorAccordion.accordionStyle.animateAccordionOpenAndClose ?? true;
137      const openTabsWithHash = drupalSettings.ckeditorAccordion.accordionStyle.openTabsWithHash ?? false;
138      for (let i = 0; i < $ckeditorAccordions.length; i++) {
139        let $accordion = $ckeditorAccordions[i];
140
141        // The first one is the correct one.
142        if (!drupalSettings.ckeditorAccordion.accordionStyle.collapseAll) {
143          $accordion.querySelector('dt:first-child').classList.add('active');
144          let dd = $accordion.querySelector('dd:first-of-type');
145          dd.classList.add('active');
146          dd.style.display = 'block';
147        }
148
149        // Turn the accordion tabs to links so that the content is accessible & can be traversed using keyboard.
150        let childDts = Array.from($accordion.children).filter(function (child) {
151          return (child.tagName.toLowerCase() == 'dt');
152        });
153        for (let x = 0; x < childDts.length; x++) {
154          let $tab = childDts[x];
155          let tabText = $tab.innerText.trim();
156          let toggleClass = $tab.classList.contains('active') ? ' active' : '';
157          let hrefAndIds = 'href="#"';
158          if (openTabsWithHash) {
159            let tabHash = encodeURIComponent(tabText.replace(/[^A-Za-z0-9]/g, ""));
160            hrefAndIds = 'href="#' + tabHash + '" id="' + tabHash + '" onclick="return false;"';
161          }
162          $tab.innerHTML = '<a class="ckeditor-accordion-toggler" ' + hrefAndIds + '><span class="ckeditor-accordion-toggle' + toggleClass + '"></span>' + tabText + '</a>';
163        }
164
165        // Wrap the accordion in a div element so that quick edit function shows the source correctly.
166        $accordion.classList.add('styled');
167        $accordion.classList.remove('ckeditor-accordion');
168        let $wrapper = document.createElement('div');
169        $wrapper.classList.add('ckeditor-accordion-container');
170        if (!doAnimate) {
171          $wrapper.classList.add('no-animations');
172        }
173        $accordion.after($wrapper);
174        $wrapper.appendChild($accordion);
175
176        // Trigger an ckeditorAccordionAttached event to let other frameworks know that the accordion has been attached.
177        let eventAccordionAttached = new Event('ckeditorAccordionAttached');
178        $accordion.dispatchEvent(eventAccordionAttached);
179
180        // Handle click events.
181        let $togglers = $accordion.querySelectorAll('.ckeditor-accordion-toggler');
182        for (let x = 0; x < $togglers.length; x++) {
183          $togglers[x].addEventListener("click", function(e) {
184            let $title = this.parentNode;
185            let $content = $title.nextElementSibling;
186            let $parent = $title.parentNode;
187
188            if (animating) {
189              // Prevent defaults.
190              e.preventDefault();
191              return;
192            }
193            animating = true;
194
195            // Clicking on open element, close it.
196            if ($title.classList.contains('active')) {
197              $title.classList.remove('active');
198              $content.classList.remove('active');
199
200              // Add to animateObjects.
201              animateObjects.push(['slideUp', $content]);
202            }
203            else {
204              if(!drupalSettings.ckeditorAccordion.accordionStyle.keepRowsOpen) {
205                let activeChilds = Array.from($parent.children).filter(function (child) {
206                  return (child.classList.contains('active'));
207                });
208                activeChilds.forEach(function(activeChild) {
209                  // Remove active class from all.
210                  activeChild.classList.remove('active');
211                  if (activeChild.tagName.toLowerCase() == 'dd') {
212                    // Slide DDs up.
213                    animateObjects.push(['slideUp', activeChild]);
214                  }
215                });
216              }
217
218              $title.classList.add('active');
219              $content.classList.add('active');
220
221              // Add to animateObjects.
222              animateObjects.push(['slideDown', $content]);
223            }
224
225            doAnimations(animateObjects);
226
227            // Prevent defaults.
228            e.preventDefault();
229          });
230        }
231
232        // Open tabs with hash if config requires.
233        if (openTabsWithHash) {
234          // Trigger hash change when clicking an anchor to an accordion tab on the same page.
235          const $hashLinks = document.querySelectorAll('a[href*="#"]:not(.ckeditor-accordion-toggler):not(.visually-hidden)');
236          for (let x = 0; x < $hashLinks.length; x++) {
237            $hashLinks[x].addEventListener('click', function (e) {
238              var parser = document.createElement('a'), hash;
239              var preventDefault = false;
240              parser.href = this.getAttribute('href');
241              hash = parser.hash;
242              if (hash) {
243                // Get the matching accordion toggler with hash.
244                var el = document.querySelector('a.ckeditor-accordion-toggler[href="' + hash + '"]');
245                if (el != null) {
246                  // Set / update hash so that the event listener below fires.
247                  if (window.location.hash === hash) {
248                    window.dispatchEvent(new Event('hashchange'));
249                  }
250                  else {
251                    window.location.hash = hash;
252                  }
253
254                  preventDefault = true;
255                }
256              }
257
258              if (preventDefault) {
259                e.preventDefault();
260              }
261            });
262          }
263
264          // Open content that matches the hash on hash change.
265          window.addEventListener('hashchange', function() {
266            var el = document.querySelector('a.ckeditor-accordion-toggler[href="' + window.location.hash + '"]');
267            if (el != null && !el.parentNode.classList.contains('active')) {
268              el.click();
269            }
270          });
271          // Trigger event once on page load so that the right accordion is open.
272          window.dispatchEvent(new Event('hashchange'));
273        }
274
275      }
276    }
277  };
278})(Drupal, drupalSettings);

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.