PageSourceSearch

https://www.ipbmr.ru/assets/tmpl/js/components/hs.dropdown.js

js ipbmr.ru collected 2026-10-07 16:55:51 UTC 14,843 bytes, 631 lines download raw bytes

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