PageSourceSearch

https://odf.univ-spn.fr/skins/odf-univ-paris13/resources/templates/common/js/components/hs.dropdown.js

js univ-spn.fr collected 2026-09-24 09:06:21 UTC 14,159 bytes, 600 lines download raw bytes

1/**
2 * Dropdown component.
3 *
4 * @author Htmlstream
5 * @version 1.0
6 */
7;
8(function ($) {
9  'use strict';
10
11  $.HSCore.components.HSDropdown = {
12
13    /**
14     * Base configuration of the component.
15     *
16     * @private
17     */
18    _baseConfig: {
19      dropdownEvent: 'click',
20      dropdownType: 'simple',
21      dropdownDuration: 300,
22      dropdownEasing: 'linear',
23      dropdownAnimationIn: 'fadeIn',
24      dropdownAnimationOut: 'fadeOut',
25      dropdownHideOnScroll: true,
26      dropdownHideOnBlur: false,
27      dropdownDelay: 350,
28      afterOpen: function (invoker) {
29      },
30      afterClose: function (invoker) {
31      }
32    },
33
34    /**
35     * Collection of all initialized items on the page.
36     *
37     * @private
38     */
39    _pageCollection: $(),
40
41    /**
42     * Initialization.
43     *
44     * @param {jQuery} collection
45     * @param {Object} config
46     *
47     * @public
48     * @return {jQuery}
49     */
50    init: function (collection, config) {
51
52      var self;
53
54      if (!collection || !collection.length) return;
55
56      self = this;
57
58      var fieldsQty;
59
60      collection.each(function (i, el) {
61
62        var $this = $(el), itemConfig;
63
64        if ($this.data('HSDropDown')) return;
65
66        itemConfig = config && $.isPlainObject(config) ?
67          $.extend(true, {}, self._baseConfig, config, $this.data()) :
68          $.extend(true, {}, self._baseConfig, $this.data());
69
70        switch (itemConfig.dropdownType) {
71
72          case 'css-animation' :
73
74            $this.data('HSDropDown', new DropdownCSSAnimation($this, itemConfig));
75
76            break;
77
78          case 'jquery-slide' :
79
80            $this.data('HSDropDown', new DropdownJSlide($this, itemConfig));
81
82            break;
83
84          default :
85
86            $this.data('HSDropDown', new DropdownSimple($this, itemConfig));
87
88        }
89
90        self._pageCollection = self._pageCollection.add($this);
91        self._bindEvents($this, itemConfig.dropdownEvent, itemConfig.dropdownDelay);
92        var DropDown = $(el).data('HSDropDown');
93
94        fieldsQty = $(DropDown.target).find('input, textarea').length;
95
96      });
97
98      $(document).on('keyup.HSDropDown', function (e) {
99
100        if (e.keyCode && e.keyCode == 27) {
101
102          self._pageCollection.each(function (i, el) {
103
104            var windW = $(window).width(),
105              optIsMobileOnly = Boolean($(el).data('is-mobile-only'));
106
107            if (!optIsMobileOnly) {
108              $(el).data('HSDropDown').hide();
109            } else if (optIsMobileOnly && windW < 769) {
110              $(el).data('HSDropDown').hide();
111            }
112
113          });
114
115        }
116
117      });
118
119      $(window).on('touchstart click', function (e) {
120
121        self._pageCollection.each(function (i, el) {
122
123          var windW = $(window).width(),
124            optIsMobileOnly = Boolean($(el).data('is-mobile-only'));
125
126          if (!optIsMobileOnly) {
127            $(el).data('HSDropDown').hide();
128          } else if (optIsMobileOnly && windW < 769) {
129            $(el).data('HSDropDown').hide();
130          }
131
132        });
133
134      });
135
136      self._pageCollection.each(function (i, el) {
137
138        var target = $(el).data('HSDropDown').config.dropdownTarget;
139
140        $(target).on('click', function(e) {
141
142          e.stopPropagation();
143
144        });
145
146      });
147
148      $(window).on('scroll.HSDropDown', function (e) {
149
150        self._pageCollection.each(function (i, el) {
151
152          var DropDown = $(el).data('HSDropDown');
153
154          if (DropDown.getOption('dropdownHideOnScroll') && fieldsQty === 0) {
155
156            DropDown.hide();
157
158          } else if (DropDown.getOption('dropdownHideOnScroll') && !(/iPhone|iPad|iPod/i.test(navigator.userAgent))) {
159
160            DropDown.hide();
161
162          }
163
164        });
165
166      });
167
168      $(window).on('resize.HSDropDown', function (e) {
169
170        if (self._resizeTimeOutId) clearTimeout(self._resizeTimeOutId);
171
172        self._resizeTimeOutId = setTimeout(function () {
173
174          self._pageCollection.each(function (i, el) {
175
176            var DropDown = $(el).data('HSDropDown');
177
178            DropDown.smartPosition(DropDown.target);
179
180          });
181
182        }, 50);
183
184      });
185
186      return collection;
187
188    },
189
190    /**
191     * Binds necessary events.
192     *
193     * @param {jQuery} $invoker
194     * @param {String} eventType
195     * @param {Number} delay
196     * @private
197     */
198    _bindEvents: function ($invoker, eventType, delay) {
199
200      var $dropdown = $($invoker.data('dropdown-target'));
201
202      // if (eventType == 'hover' && !_isTouch()) {
203      if (eventType == 'hover' && !_isTouch()) {
204
205        $invoker.on('mouseenter.HSDropDown', function (e) {
206
207          var $invoker = $(this),
208            HSDropDown = $invoker.data('HSDropDown');
209
210          if (!HSDropDown) return;
211
212          if (HSDropDown.dropdownTimeOut) clearTimeout(HSDropDown.dropdownTimeOut);
213          HSDropDown.show();
214
215        })
216          .on('touchend.HSDropDown mouseleave.HSDropDown', function (e) {
217
218            var $invoker = $(this),
219              HSDropDown = $invoker.data('HSDropDown');
220
221            if (!HSDropDown) return;
222
223            HSDropDown.dropdownTimeOut = setTimeout(function () {
224
225              HSDropDown.hide();
226
227            }, delay);
228
229          });
230
231        if ($dropdown.length) {
232
233          $dropdown.on('mouseenter.HSDropDown', function (e) {
234
235            var HSDropDown = $invoker.data('HSDropDown');
236
237            if (HSDropDown.dropdownTimeOut) clearTimeout(HSDropDown.dropdownTimeOut);
238            HSDropDown.show();
239
240          })
241            .on('mouseleave.HSDropDown', function (e) {
242
243              var HSDropDown = $invoker.data('HSDropDown');
244
245              HSDropDown.dropdownTimeOut = setTimeout(function () {
246                HSDropDown.hide();
247              }, delay);
248
249            });
250        }
251
252      }
253      else {
254
255        $invoker.on('click.HSDropDown', function (e) {
256
257          var $curInvoker = $(this);
258
259          if (!$curInvoker.data('HSDropDown')) return;
260
261          if ($('[data-dropdown-target].active').length) {
262            $('[data-dropdown-target].active').data('HSDropDown').toggle();
263          }
264
265          $curInvoker.data('HSDropDown').toggle();
266
267          e.stopPropagation();
268          e.preventDefault();
269
270        });
271
272      }
273
274    }
275  };
276
277  function _isTouch() {
278    return 'ontouchstart' in window;
279  }
280
281  /**
282   * Abstract Dropdown class.
283   *
284   * @param {jQuery} element
285   * @param {Object} config
286   * @abstract
287   */
288  function AbstractDropdown(element, config) {
289
290    if (!element.length) return false;
291
292    this.element = element;
293    this.config = config;
294
295    this.target = $(this.element.data('dropdown-target'));
296
297    this.allInvokers = $('[data-dropdown-target="' + this.element.data('dropdown-target') + '"]');
298
299    this.toggle = function () {
300      if (!this.target.length) return this;
301
302      if (this.defaultState) {
303        this.show();
304      }
305      else {
306        this.hide();
307      }
308
309      return this;
310    };
311
312    this.smartPosition = function (target) {
313
314      if (target.data('baseDirection')) {
315        target.css(
316          target.data('baseDirection').direction,
317          target.data('baseDirection').value
318        );
319      }
320      
321      target.removeClass('u-dropdown--reverse-y');
322
323      var $w = $(window),
324        styles = getComputedStyle(target.get(0)),
325        direction = Math.abs(parseInt(styles.left, 10)) < 40 ? 'left' : 'right',
326        targetOuterGeometry = target.offset();
327
328      // horizontal axis
329      if (direction == 'right') {
330
331        if (!target.data('baseDirection')) target.data('baseDirection', {
332          direction: 'right',
333          value: parseInt(styles.right, 10)
334        });
335
336        if (targetOuterGeometry.left < 0) {
337
338          target.css(
339            'right',
340            (parseInt(target.css('right'), 10) - (targetOuterGeometry.left - 10 )) * -1
341          );
342
343        }
344
345      }
346      else {
347
348        if (!target.data('baseDirection')) target.data('baseDirection', {
349          direction: 'left',
350          value: parseInt(styles.left, 10)
351        });
352
353        if (targetOuterGeometry.left + target.outerWidth() > $w.width()) {
354
355          target.css(
356            'left',
357            (parseInt(target.css('left'), 10) - (targetOuterGeometry.left + target.outerWidth() + 10 - $w.width()))
358          );
359
360        }
361
362      }
363
364      // vertical axis
365      if (targetOuterGeometry.top + target.outerHeight() - $w.scrollTop() > $w.height()) {
366        this.element.addClass('u-invoker--reverse-y');
367        target.addClass('u-dropdown--reverse-y');
368      }
369
370    };
371
372    this.getOption = function (option) {
373      return this.config[option] ? this.config[option] : null;
374    };
375
376    return true;
377  }
378
379
380  /**
381   * DropdownSimple constructor.
382   *
383   * @param {jQuery} element
384   * @param {Object} config
385   * @constructor
386   */
387  function DropdownSimple(element, config) {
388    if (!AbstractDropdown.call(this, element, config)) return;
389
390    Object.defineProperty(this, 'defaultState', {
391      get: function () {
392        return this.target.hasClass('u-dropdown--hidden');
393      }
394    });
395
396    this.target.addClass('u-dropdown--simple');
397
398    this.hide();
399  }
400
401  /**
402   * Shows dropdown.
403   *
404   * @public
405   * @return {DropdownSimple}
406   */
407  DropdownSimple.prototype.show = function () {
408
409    var activeEls = $(this)[0].config.dropdownTarget;
410    
411    $('[data-dropdown-target="' + activeEls + '"]').addClass('active');
412
413    this.smartPosition(this.target);
414
415    this.target.removeClass('u-dropdown--hidden');
416    if (this.allInvokers.length) this.allInvokers.attr('aria-expanded', 'true');
417    this.config.afterOpen.call(this.target, this.element);
418
419    return this;
420  }
421
422  /**
423   * Hides dropdown.
424   *
425   * @public
426   * @return {DropdownSimple}
427   */
428  DropdownSimple.prototype.hide = function () {
429
430    var activeEls = $(this)[0].config.dropdownTarget;
431  
432    $('[data-dropdown-target="' + activeEls + '"]').removeClass('u-invoker--reverse-y');
433    $('[data-dropdown-target="' + activeEls + '"]').removeClass('active');
434
435    this.target.addClass('u-dropdown--hidden');
436    if (this.allInvokers.length) this.allInvokers.attr('aria-expanded', 'false');
437    this.config.afterClose.call(this.target, this.element);
438
439    return this;
440  }
441
442  /**
443   * DropdownCSSAnimation constructor.
444   *
445   * @param {jQuery} element
446   * @param {Object} config
447   * @constructor
448   */
449  function DropdownCSSAnimation(element, config) {
450    if (!AbstractDropdown.call(this, element, config)) return;
451
452    var self = this;
453
454    this.target
455      .addClass('u-dropdown--css-animation u-dropdown--hidden')
456      .css('animation-duration', self.config.dropdownDuration + 'ms');
457
458    Object.defineProperty(this, 'defaultState', {
459      get: function () {
460        return this.target.hasClass('u-dropdown--hidden');
461      }
462    });
463
464    if (this.target.length) {
465
466      this.target.on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function (e) {
467
468        if (self.target.hasClass(self.config.dropdownAnimationOut)) {
469          self.target.removeClass(self.config.dropdownAnimationOut)
470            .addClass('u-dropdown--hidden');
471
472
473          if (self.allInvokers.length) self.allInvokers.attr('aria-expanded', 'false');
474
475          self.config.afterClose.call(self.target, self.element);
476        }
477
478        if (self.target.hasClass(self.config.dropdownAnimationIn)) {
479
480          if (self.allInvokers.length) self.allInvokers.attr('aria-expanded', 'true');
481
482          self.config.afterOpen.call(self.target, self.element);
483        }
484
485        e.preventDefault();
486        e.stopPropagation();
487      });
488
489    }
490  }
491
492  /**
493   * Shows dropdown.
494   *
495   * @public
496   * @return {DropdownCSSAnimation}
497   */
498  DropdownCSSAnimation.prototype.show = function () {
499
500    var activeEls = $(this)[0].config.dropdownTarget;
501
502    $('[data-dropdown-target="' + activeEls + '"]').addClass('active');
503
504    this.smartPosition(this.target);
505
506    this.target.removeClass('u-dropdown--hidden')
507      .removeClass(this.config.dropdownAnimationOut)
508      .addClass(this.config.dropdownAnimationIn);
509
510  }
511
512  /**
513   * Hides dropdown.
514   *
515   * @public
516   * @return {DropdownCSSAnimation}
517   */
518  DropdownCSSAnimation.prototype.hide = function () {
519
520    var activeEls = $(this)[0].config.dropdownTarget;
521  
522    $('[data-dropdown-target="' + activeEls + '"]').removeClass('u-invoker--reverse-y');
523    $('[data-dropdown-target="' + activeEls + '"]').removeClass('active');
524
525    this.target.removeClass(this.config.dropdownAnimationIn)
526      .addClass(this.config.dropdownAnimationOut);
527
528  }
529
530  /**
531   * DropdownSlide constructor.
532   *
533   * @param {jQuery} element
534   * @param {Object} config
535   * @constructor
536   */
537  function DropdownJSlide(element, config) {
538    if (!AbstractDropdown.call(this, element, config)) return;
539
540    this.target.addClass('u-dropdown--jquery-slide u-dropdown--hidden').hide();
541
542    Object.defineProperty(this, 'defaultState', {
543      get: function () {
544        return this.target.hasClass('u-dropdown--hidden');
545      }
546    });
547  }
548
549  /**
550   * Shows dropdown.
551   *
552   * @public
553   * @return {DropdownJSlide}
554   */
555  DropdownJSlide.prototype.show = function () {
556
557    var self = this;
558
559    var activeEls = $(this)[0].config.dropdownTarget;
560
561    $('[data-dropdown-target="' + activeEls + '"]').addClass('active');
562
563    this.smartPosition(this.target);
564
565    this.target.removeClass('u-dropdown--hidden').stop().slideDown({
566      duration: self.config.dropdownDuration,
567      easing: self.config.dropdownEasing,
568      complete: function () {
569        self.config.afterOpen.call(self.target, self.element);
570      }
571    });
572
573  }
574
575  /**
576   * Hides dropdown.
577   *
578   * @public
579   * @return {DropdownJSlide}
580   */
581  DropdownJSlide.prototype.hide = function () {
582
583    var self = this;
584
585    var activeEls = $(this)[0].config.dropdownTarget;
586
587    $('[data-dropdown-target="' + activeEls + '"]').removeClass('active');
588
589    this.target.stop().slideUp({
590      duration: self.config.dropdownDuration,
591      easing: self.config.dropdownEasing,
592      complete: function () {
593        self.config.afterClose.call(self.target, self.element);
594        self.target.addClass('u-dropdown--hidden');
595      }
596    });
597
598  }
599
600})(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.