PageSourceSearch

https://www.glotechrepairs.co.uk/assets/js/components/hs.dropdown.js

js glotechrepairs.co.uk collected 2026-10-04 04:39:54 UTC 11,722 bytes, 522 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
20      dropdownEvent: 'click',
21      dropdownType: 'simple',
22      dropdownDuration: 300,
23      dropdownEasing: 'linear',
24      dropdownAnimationIn: 'fadeIn',
25      dropdownAnimationOut: 'fadeOut',
26      dropdownHideOnScroll: true,
27      dropdownHideOnBlur: false,
28      dropdownDelay: 350,
29
30      afterOpen: function(invoker){},
31      afterClose: function(invoker){}
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      collection.each(function(i, el){
59
60        var $this = $(el), itemConfig;
61
62        if( $this.data('HSDropDown') ) return;
63
64        itemConfig = config && $.isPlainObject(config) ? 
65                     $.extend(true, {}, self._baseConfig, config, $this.data()) :
66                     $.extend(true, {}, self._baseConfig, $this.data());
67
68        switch( itemConfig.dropdownType ) {
69
70          case 'css-animation' :
71
72            $this.data('HSDropDown', new DropdownCSSAnimation($this, itemConfig));
73
74          break;
75
76          case 'jquery-slide' : 
77
78            $this.data('HSDropDown', new DropdownJSlide($this, itemConfig));
79
80          break;
81
82          default : 
83
84            $this.data('HSDropDown', new DropdownSimple($this, itemConfig));
85
86        }
87
88        self._pageCollection = self._pageCollection.add( $this );
89        self._bindEvents( $this, itemConfig.dropdownEvent, itemConfig.dropdownDelay );
90
91      });
92
93      $(document).on('keyup.HSDropDown', function(e){
94
95        if(e.keyCode && e.keyCode == 27) {
96
97          self._pageCollection.each( function(i, el){
98
99            $(el).data('HSDropDown').hide();
100
101          } );
102
103        }
104
105      });
106
107      $(window).on('scroll.HSDropDown', function(e){
108
109        self._pageCollection.each(function(i, el){
110
111          var DropDown = $(el).data('HSDropDown');
112
113          if(DropDown.getOption('dropdownHideOnScroll')) {
114
115            DropDown.hide();
116
117          }
118
119        });
120
121      });
122
123      $(window).on('resize.HSDropDown', function(e){
124
125        if(self._resizeTimeOutId) clearTimeout(self._resizeTimeOutId);
126
127        self._resizeTimeOutId = setTimeout(function(){
128
129          self._pageCollection.each(function(i, el){
130
131            var DropDown = $(el).data('HSDropDown');
132
133            DropDown.smartPosition(DropDown.target);
134
135          });
136
137        }, 50);
138
139      });
140
141      return collection;
142
143    },
144
145    /**
146     * Binds necessary events.
147     * 
148     * @param {jQuery} $invoker
149     * @param {String} eventType
150     * @param {Number} delay
151     * @private 
152     */
153    _bindEvents: function( $invoker, eventType, delay ) {
154
155      var $dropdown = $($invoker.data('dropdown-target'));
156
157      if( eventType == 'hover' && !_isTouch() ) {
158
159        $invoker.on('mouseenter.HSDropDown', function(e) {
160
161          var $invoker = $(this),
162              HSDropDown = $invoker.data('HSDropDown');
163
164          if( !HSDropDown ) return;
165
166          if( HSDropDown.dropdownTimeOut ) clearTimeout( HSDropDown.dropdownTimeOut );
167          HSDropDown.show();
168
169        })
170        .on('mouseleave.HSDropDown', function(e) {
171
172          var $invoker = $(this),
173              HSDropDown = $invoker.data('HSDropDown');
174
175          if( !HSDropDown ) return;
176
177          HSDropDown.dropdownTimeOut = setTimeout(function(){
178
179            HSDropDown.hide();
180
181          }, delay);
182
183        });
184
185        if( $dropdown.length ) {
186          $dropdown.on('mouseenter.HSDropDown', function(e){
187
188            var HSDropDown = $invoker.data('HSDropDown');
189
190            if( HSDropDown.dropdownTimeOut ) clearTimeout( HSDropDown.dropdownTimeOut );
191            HSDropDown.show();
192
193          })
194          .on('mouseleave.HSDropDown', function(e) {
195
196            var HSDropDown = $invoker.data('HSDropDown');
197
198            HSDropDown.dropdownTimeOut = setTimeout(function() {
199              HSDropDown.hide();
200            }, delay);
201
202          });
203        }
204
205      }
206      else {
207
208        $invoker.on('click.HSDropDown', function(e){
209
210          var $invoker = $(this);
211
212          if( !$invoker.data('HSDropDown') ) return;
213
214          $invoker.data('HSDropDown').toggle();
215
216          e.stopPropagation();
217          e.preventDefault();
218
219        });
220
221      }
222
223    }
224  };
225
226  function _isTouch() {
227    return 'ontouchstart' in window;
228  }
229
230  /**
231   * Abstract Dropdown class.
232   *
233   * @param {jQuery} element
234   * @param {Object} config
235   * @abstract 
236   */
237  function AbstractDropdown(element, config) {
238
239    if( !element.length ) return false;
240
241    this.element = element;
242    this.config = config;
243
244    this.target = $(this.element.data('dropdown-target'));
245
246    this.allInvokers = $('[data-dropdown-target="'+this.element.data('dropdown-target')+'"]');
247
248    this.toggle = function() {
249      if( !this.target.length ) return this;
250
251      if( this.defaultState ) {
252        this.show();
253      }
254      else {
255        this.hide();
256      }
257
258      return this;
259    };
260
261    this.smartPosition = function( target ) {
262
263      if(target.data('baseDirection')) {
264        target.css(
265          target.data('baseDirection').direction,
266          target.data('baseDirection').value
267        );
268      }
269
270      target.removeClass('u-dropdown--reverse-y');
271
272      var $w = $(window),
273          styles = getComputedStyle(target.get(0)),
274          direction = Math.abs( parseInt( styles.left, 10) ) < 40 ? 'left' : 'right',
275          targetOuterGeometry = target.offset();
276
277      // horizontal axis
278      if(direction == 'right') {
279
280        if( !target.data('baseDirection') ) target.data('baseDirection', {
281          direction: 'right',
282          value: parseInt( styles.right, 10)
283        } );
284
285        if( targetOuterGeometry.left < 0 ) {
286
287          target.css(
288            'right',
289            (parseInt( target.css('right'), 10 ) - (targetOuterGeometry.left - 10 )) * -1
290          );
291
292        }
293
294      }
295      else {
296
297        if( !target.data('baseDirection') ) target.data('baseDirection', {
298          direction: 'left',
299          value: parseInt( styles.left, 10)
300        } );
301
302        if( targetOuterGeometry.left + target.outerWidth() > $w.width() ) {
303
304          target.css(
305            'left',
306            (parseInt( target.css('left'), 10 ) - (targetOuterGeometry.left + target.outerWidth() + 10 - $w.width()))
307          );
308
309        }
310
311      }
312
313      // vertical axis
314      if( targetOuterGeometry.top + target.outerHeight() - $w.scrollTop() > $w.height() ) {
315        target.addClass('u-dropdown--reverse-y');
316      }
317
318    };
319
320    this.getOption = function(option){
321      return this.config[option] ? this.config[option] : null;
322    };
323
324    return true;
325  }
326
327
328  /**
329   * DropdownSimple constructor.
330   *
331   * @param {jQuery} element
332   * @param {Object} config
333   * @constructor 
334   */
335  function DropdownSimple(element, config) {
336    if( !AbstractDropdown.call(this, element, config) ) return;
337
338    Object.defineProperty(this, 'defaultState', {
339      get: function() {
340        return this.target.hasClass('u-dropdown--hidden');
341      }
342    });
343
344    this.target.addClass('u-dropdown--simple');
345
346    this.hide();
347  }
348
349  /**
350   * Shows dropdown.
351   *
352   * @public 
353   * @return {DropdownSimple}
354   */
355  DropdownSimple.prototype.show = function() {
356
357    this.smartPosition(this.target);
358
359    this.target.removeClass('u-dropdown--hidden');
360    if( this.allInvokers.length ) this.allInvokers.attr('aria-expanded', 'true');
361    this.config.afterOpen.call(this.target, this.element);
362
363    return this;
364  }
365
366  /**
367   * Hides dropdown.
368   *
369   * @public 
370   * @return {DropdownSimple}
371   */
372  DropdownSimple.prototype.hide = function() {
373
374    this.target.addClass('u-dropdown--hidden');
375    if( this.allInvokers.length ) this.allInvokers.attr('aria-expanded', 'false');
376    this.config.afterClose.call(this.target, this.element);
377
378    return this;
379  }
380
381  /**
382   * DropdownCSSAnimation constructor.
383   *
384   * @param {jQuery} element
385   * @param {Object} config
386   * @constructor 
387   */
388  function DropdownCSSAnimation(element, config) {
389    if( !AbstractDropdown.call(this, element, config) ) return;
390
391    var self = this;
392
393    this.target
394        .addClass('u-dropdown--css-animation u-dropdown--hidden')
395        .css('animation-duration', self.config.dropdownDuration + 'ms');
396
397    Object.defineProperty(this, 'defaultState', {
398      get: function() {
399        return this.target.hasClass('u-dropdown--hidden');
400      }
401    });
402
403    if(this.target.length) {
404
405      this.target.on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(e){
406
407        if( self.target.hasClass( self.config.dropdownAnimationOut ) ) {
408          self.target.removeClass( self.config.dropdownAnimationOut )
409                     .addClass('u-dropdown--hidden');
410
411
412          if( self.allInvokers.length ) self.allInvokers.attr('aria-expanded', 'false');
413
414          self.config.afterClose.call(self.target, self.element);
415        }
416
417        if( self.target.hasClass( self.config.dropdownAnimationIn ) ) {
418          
419          if( self.allInvokers.length ) self.allInvokers.attr('aria-expanded', 'true');
420
421          self.config.afterOpen.call(self.target, self.element);
422        }
423
424        e.preventDefault();
425        e.stopPropagation();
426      });
427
428    }
429  }
430
431  /**
432   * Shows dropdown.
433   *
434   * @public 
435   * @return {DropdownCSSAnimation}
436   */
437  DropdownCSSAnimation.prototype.show = function() {
438
439    this.smartPosition(this.target);
440
441    this.target.removeClass( 'u-dropdown--hidden' )
442               .removeClass( this.config.dropdownAnimationOut )
443               .addClass( this.config.dropdownAnimationIn );
444
445  }
446
447  /**
448   * Hides dropdown.
449   *
450   * @public 
451   * @return {DropdownCSSAnimation}
452   */
453  DropdownCSSAnimation.prototype.hide = function() {
454
455    this.target.removeClass( this.config.dropdownAnimationIn )
456               .addClass( this.config.dropdownAnimationOut );
457
458  }
459
460  /**
461   * DropdownSlide constructor.
462   *
463   * @param {jQuery} element
464   * @param {Object} config
465   * @constructor 
466   */
467  function DropdownJSlide(element, config) {
468    if( !AbstractDropdown.call(this, element, config) ) return;
469
470    this.target.addClass('u-dropdown--jquery-slide u-dropdown--hidden').hide();
471
472    Object.defineProperty(this, 'defaultState', {
473      get: function() {
474        return this.target.hasClass('u-dropdown--hidden');
475      }
476    });
477  }
478
479  /**
480   * Shows dropdown.
481   *
482   * @public 
483   * @return {DropdownJSlide}
484   */
485  DropdownJSlide.prototype.show = function() {
486
487    var self = this;
488
489    this.smartPosition(this.target);
490
491    this.target.removeClass('u-dropdown--hidden').stop().slideDown({
492      duration: self.config.dropdownDuration,
493      easing: self.config.dropdownEasing,
494      complete: function(){
495        self.config.afterOpen.call(self.target, self.element);
496      }
497    });
498
499  }
500
501  /**
502   * Hides dropdown.
503   *
504   * @public 
505   * @return {DropdownJSlide}
506   */
507  DropdownJSlide.prototype.hide = function() {
508
509    var self = this;
510
511    this.target.stop().slideUp({
512      duration: self.config.dropdownDuration,
513      easing: self.config.dropdownEasing,
514      complete: function(){
515        self.config.afterClose.call(self.target, self.element);
516        self.target.addClass('u-dropdown--hidden');
517      }
518    });
519
520  }
521
522})(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.