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.