1jQuery(function () { 2 initMobileNav(); 3 initTouchNav(); 4 initOpenClose(); 5 initStickyScrollBlock(); 6 initDropDownClasses(); 7}); 8 9// mobile menu init 10function initMobileNav() { 11 jQuery('body').mobileNav({ 12 menuActiveClass: 'nav-active', 13 menuOpener: '.nav-opener' 14 }); 15} 16 17// handle dropdowns on mobile devices 18function initTouchNav() { 19 jQuery('#nav').each(function() { 20 new TouchNav({ 21 navBlock: this, 22 menuDrop: '.drop' 23 }); 24 }); 25} 26 27// open-close init 28function initOpenClose() { 29 jQuery('.footer-link-wrap .col').openClose({ 30 activeClass: 'active', 31 opener: '.title', 32 slider: '.menu', 33 animSpeed: 400, 34 effect: 'slide' 35 }); 36} 37 38// add classes if item has dropdown 39function initDropDownClasses() { 40 jQuery('#nav li').each(function() { 41 var item = jQuery(this); 42 var drop = item.find('.drop'); 43 var link = item.find('a').eq(0); 44 if (drop.length) { 45 item.addClass('has-drop-down'); 46 if (link.length) link.addClass('has-drop-down-a'); 47 } 48 }); 49} 50 51// initialize fixed blocks on scroll 52function initStickyScrollBlock() { 53 ResponsiveHelper.addRange({ 54 '768..': { 55 on: function() { 56 jQuery('.block-share-link').stickyScrollBlock({ 57 setBoxHeight: false, 58 activeClass: 'fixed-position', 59 container: '.content-wrap', 60 positionType: 'fixed', 61 extraTop: function() { 62 var totalHeight = 0; 63 jQuery('0').each(function() { 64 totalHeight += jQuery(this).outerHeight(); 65 }); 66 return totalHeight; 67 } 68 }); 69 }, 70 off: function() { 71 jQuery('.block-share-link').stickyScrollBlock('destroy'); 72 } 73 } 74 }); 75} 76 77/* 78 * jQuery sticky box plugin 79 */ 80;(function($, $win) { 81 'use strict'; 82 83 function StickyScrollBlock($stickyBox, options) { 84 this.options = options; 85 this.$stickyBox = $stickyBox; 86 this.init(); 87 } 88 89 var StickyScrollBlockPrototype = { 90 init: function() { 91 this.findElements(); 92 this.attachEvents(); 93 this.makeCallback('onInit'); 94 }, 95 96 findElements: function() { 97 // find parent container in which will be box move 98 this.$container = this.$stickyBox.closest(this.options.container); 99 // define box wrap flag 100 this.isWrap = this.options.positionType === 'fixed' && this.options.setBoxHeight; 101 // define box move flag 102 this.moveInContainer = !!this.$container.length; 103 // wrapping box to set place in content 104 if (this.isWrap) { 105 this.$stickyBoxWrap = this.$stickyBox.wrap('<div class="' + this.getWrapClass() + '"/>').parent(); 106 } 107 //define block to add active class 108 this.parentForActive = this.getParentForActive(); 109 this.isInit = true; 110 }, 111 112 attachEvents: function() { 113 var self = this; 114 115 // bind events 116 this.onResize = function() { 117 if (!self.isInit) return; 118 self.resetState(); 119 self.recalculateOffsets(); 120 self.checkStickyPermission(); 121 self.scrollHandler(); 122 }; 123 124 this.onScroll = function() { 125 self.scrollHandler(); 126 }; 127 128 // initial handler call 129 this.onResize(); 130 131 // handle events 132 $win.on('load resize orientationchange', this.onResize) 133 .on('scroll', this.onScroll); 134 }, 135 136 defineExtraTop: function() { 137 // define box's extra top dimension 138 var extraTop; 139 140 if (typeof this.options.extraTop === 'number') { 141 extraTop = this.options.extraTop; 142 } else if (typeof this.options.extraTop === 'function') { 143 extraTop = this.options.extraTop(); 144 } 145 146 this.extraTop = this.options.positionType === 'absolute' ? 147 extraTop : 148 Math.min(this.winParams.height - this.data.boxFullHeight, extraTop); 149 }, 150 151 checkStickyPermission: function() { 152 // check the permission to set sticky 153 this.isStickyEnabled = this.moveInContainer ? 154 this.data.containerOffsetTop + this.data.containerHeight > this.data.boxFullHeight + this.data.boxOffsetTop + this.options.extraBottom : 155 true; 156 }, 157 158 getParentForActive: function() { 159 if (this.isWrap) { 160 return this.$stickyBoxWrap; 161 } 162 163 if (this.$container.length) { 164 return this.$container; 165 } 166 167 return this.$stickyBox; 168 }, 169 170 getWrapClass: function() { 171 // get set of container classes 172 try { 173 return this.$stickyBox.attr('class').split(' ').map(function(name) { 174 return 'sticky-wrap-' + name; 175 }).join(' '); 176 } catch (err) { 177 return 'sticky-wrap'; 178 } 179 }, 180 181 resetState: function() { 182 // reset dimensions and state 183 this.stickyFlag = false;
184 this.$stickyBox.css({ 185 '-webkit-transition': '', 186 '-webkit-transform': '', 187 transition: '', 188 transform: '', 189 position: '', 190 width: '', 191 left: '', 192 top: '' 193 }).removeClass(this.options.activeClass); 194 195 if (this.isWrap) { 196 this.$stickyBoxWrap.removeClass(this.options.activeClass).removeAttr('style'); 197 } 198 199 if (this.moveInContainer) { 200 this.$container.removeClass(this.options.activeClass); 201 } 202 }, 203 204 recalculateOffsets: function() { 205 // define box and container dimensions 206 this.winParams = this.getWindowParams(); 207 208 this.data = $.extend( 209 this.getBoxOffsets(), 210 this.getContainerOffsets() 211 ); 212 213 this.defineExtraTop(); 214 }, 215 216 getBoxOffsets: function() { 217 function offetTop(obj){ 218 obj.top = 0; 219 return obj 220 } 221 var boxOffset = this.$stickyBox.css('position') ==='fixed' ? offetTop(this.$stickyBox.offset()) : this.$stickyBox.offset(); 222 var boxPosition = this.$stickyBox.position(); 223 224 return { 225 // sticky box offsets 226 boxOffsetLeft: boxOffset.left, 227 boxOffsetTop: boxOffset.top, 228 // sticky box positions 229 boxTopPosition: boxPosition.top, 230 boxLeftPosition: boxPosition.left, 231 // sticky box width/height 232 boxFullHeight: this.$stickyBox.outerHeight(true), 233 boxHeight: this.$stickyBox.outerHeight(), 234 boxWidth: this.$stickyBox.outerWidth() 235 }; 236 }, 237 238 getContainerOffsets: function() { 239 var containerOffset = this.moveInContainer ? this.$container.offset() : null; 240 241 return containerOffset ? { 242 // container offsets 243 containerOffsetLeft: containerOffset.left, 244 containerOffsetTop: containerOffset.top, 245 // container height 246 containerHeight: this.$container.outerHeight() 247 } : {}; 248 }, 249 250 getWindowParams: function() { 251 return { 252 height: window.innerHeight || document.documentElement.clientHeight 253 }; 254 }, 255 256 makeCallback: function(name) { 257 if (typeof this.options[name] === 'function') { 258 var args = Array.prototype.slice.call(arguments); 259 args.shift(); 260 this.options[name].apply(this, args); 261 } 262 }, 263 264 destroy: function() { 265 this.isInit = false; 266 // remove event handlers and styles 267 $win.off('load resize orientationchange', this.onResize) 268 .off('scroll', this.onScroll); 269 this.resetState(); 270 this.$stickyBox.removeData('StickyScrollBlock'); 271 if (this.isWrap) { 272 this.$stickyBox.unwrap(); 273 } 274 this.makeCallback('onDestroy'); 275 } 276 }; 277 278 var stickyMethods = { 279 fixed: { 280 scrollHandler: function() { 281 this.winScrollTop = $win.scrollTop(); 282 var isActiveSticky = this.winScrollTop - 283 (this.options.showAfterScrolled ? this.extraTop : 0) - 284 (this.options.showAfterScrolled ? this.data.boxHeight + this.extraTop : 0) > 285 this.data.boxOffsetTop - this.extraTop; 286 287 if (isActiveSticky) { 288 this.isStickyEnabled && this.stickyOn(); 289 } else { 290 this.stickyOff(); 291 } 292 }, 293 294 stickyOn: function() { 295 if (!this.stickyFlag) { 296 this.stickyFlag = true; 297 this.parentForActive.addClass(this.options.activeClass); 298 this.$stickyBox.css({ 299 width: this.data.boxWidth, 300 position: this.options.positionType 301 }); 302 if (this.isWrap) { 303 this.$stickyBoxWrap.css({ 304 height: this.data.boxFullHeight 305 }); 306 } 307 this.makeCallback('fixedOn'); 308 } 309 this.setDynamicPosition(); 310 }, 311 312 stickyOff: function() { 313 if (this.stickyFlag) { 314 this.stickyFlag = false; 315 this.resetState(); 316 this.makeCallback('fixedOff'); 317 } 318 }, 319 320 setDynamicPosition: function() { 321 this.$stickyBox.css({ 322 top: this.getTopPosition(), 323 left: this.data.boxOffsetLeft - $win.scrollLeft() 324 }); 325 }, 326 327 getTopPosition: function() { 328 if (this.moveInContainer) { 329 var currScrollTop = this.winScrollTop + this.data.boxHeight + this.options.extraBottom; 330 331 return Math.min(this.extraTop, (this.data.containerHeight + this.data.containerOffsetTop) - currScrollTop); 332 } else { 333 return this.extraTop; 334 } 335 } 336 }, 337 absolute: { 338 scrollHandler: function() { 339 this.winScrollTop = $win.scrollTop(); 340 var isActiveSticky = this.winScrollTop > this.data.boxOffsetTop - this.extraTop; 341 342 if (isActiveSticky) { 343 this.isStickyEnabled && this.stickyOn(); 344 } else { 345 this.stickyOff(); 346 } 347 }, 348 349 stickyOn: function() { 350 if (!this.stickyFlag) { 351 this.stickyFlag = true; 352 this.parentForActive.addClass(this.options.activeClass); 353 this.$stickyBox.css({ 354 width: this.data.boxWidth, 355 transition: 'transform ' + this.options.animSpeed + 's ease', 356 '-webkit-transition': 'transform ' + this.options.animSpeed + 's ease', 357 }); 358 359 if (this.isWrap) { 360 this.$stickyBoxWrap.css({ 361 height: this.data.boxFullHeight 362 }); 363 } 364 365 this.makeCallback('fixedOn'); 366 } 367 368 this.clearTimer(); 369 this.timer = setTimeout(function() { 370 this.setDynamicPosition(); 371 }.bind(this), this.options.animDelay * 1000); 372 }, 373 374 stickyOff: function() { 375 if (this.stickyFlag) { 376 this.clearTimer(); 377 this.stickyFlag = false;
378 379 this.timer = setTimeout(function() { 380 this.setDynamicPosition(); 381 setTimeout(function() { 382 this.resetState(); 383 }.bind(this), this.options.animSpeed * 1000); 384 }.bind(this), this.options.animDelay * 1000); 385 this.makeCallback('fixedOff'); 386 } 387 }, 388 389 clearTimer: function() { 390 clearTimeout(this.timer); 391 }, 392 393 setDynamicPosition: function() { 394 var topPosition = Math.max(0, this.getTopPosition()); 395 396 this.$stickyBox.css({ 397 transform: 'translateY(' + topPosition + 'px)', 398 '-webkit-transform': 'translateY(' + topPosition + 'px)' 399 }); 400 }, 401 402 getTopPosition: function() { 403 var currTopPosition = this.winScrollTop - this.data.boxOffsetTop + this.extraTop; 404 405 if (this.moveInContainer) { 406 var currScrollTop = this.winScrollTop + this.data.boxHeight + this.options.extraBottom; 407 var diffOffset = Math.abs(Math.min(0, (this.data.containerHeight + this.data.containerOffsetTop) - currScrollTop - this.extraTop)); 408 409 return currTopPosition - diffOffset; 410 } else { 411 return currTopPosition; 412 } 413 } 414 } 415 }; 416 417 // jQuery plugin interface 418 $.fn.stickyScrollBlock = function(opt) { 419 var args = Array.prototype.slice.call(arguments); 420 var method = args[0]; 421 422 var options = $.extend({ 423 container: null, 424 positionType: 'fixed', // 'fixed' or 'absolute' 425 activeClass: 'fixed-position', 426 setBoxHeight: true, 427 showAfterScrolled: false, 428 extraTop: 0, 429 extraBottom: 0, 430 animDelay: 0.1, 431 animSpeed: 0.2 432 }, opt); 433 434 return this.each(function() { 435 var $stickyBox = jQuery(this); 436 var instance = $stickyBox.data('StickyScrollBlock'); 437 438 if (typeof opt === 'object' || typeof opt === 'undefined') { 439 StickyScrollBlock.prototype = $.extend(stickyMethods[options.positionType], StickyScrollBlockPrototype); 440 $stickyBox.data('StickyScrollBlock', new StickyScrollBlock($stickyBox, options)); 441 } else if (typeof method === 'string' && instance) { 442 if (typeof instance[method] === 'function') { 443 args.shift(); 444 instance[method].apply(instance, args); 445 } 446 } 447 }); 448 }; 449 450 // module exports 451 window.StickyScrollBlock = StickyScrollBlock; 452}(jQuery, jQuery(window))); 453 454/* 455 * Responsive Layout helper 456 */ 457window.ResponsiveHelper = (function($){ 458 // init variables 459 var handlers = [], 460 prevWinWidth, 461 win = $(window), 462 nativeMatchMedia = false; 463 464 // detect match media support 465 if(window.matchMedia) { 466 if(window.Window && window.matchMedia === Window.prototype.matchMedia) { 467 nativeMatchMedia = true; 468 } else if(window.matchMedia.toString().indexOf('native') > -1) { 469 nativeMatchMedia = true; 470 } 471 } 472 473 // prepare resize handler 474 function resizeHandler() { 475 var winWidth = win.width(); 476 if(winWidth !== prevWinWidth) { 477 prevWinWidth = winWidth; 478 479 // loop through range groups 480 $.each(handlers, function(index, rangeObject){ 481 // disable current active area if needed 482 $.each(rangeObject.data, function(property, item) { 483 if(item.currentActive && !matchRange(item.range[0], item.range[1])) { 484 item.currentActive = false; 485 if(typeof item.disableCallback === 'function') { 486 item.disableCallback(); 487 } 488 } 489 }); 490 491 // enable areas that match current width 492 $.each(rangeObject.data, function(property, item) { 493 if(!item.currentActive && matchRange(item.range[0], item.range[1])) { 494 // make callback 495 item.currentActive = true; 496 if(typeof item.enableCallback === 'function') { 497 item.enableCallback(); 498 } 499 } 500 }); 501 }); 502 } 503 } 504 win.bind('load resize orientationchange', resizeHandler); 505 506 // test range 507 function matchRange(r1, r2) { 508 var mediaQueryString = ''; 509 if(r1 > 0) { 510 mediaQueryString += '(min-width: ' + r1 + 'px)'; 511 } 512 if(r2 < Infinity) { 513 mediaQueryString += (mediaQueryString ? ' and ' : '') + '(max-width: ' + r2 + 'px)'; 514 } 515 return matchQuery(mediaQueryString, r1, r2); 516 } 517 518 // media query function 519 function matchQuery(query, r1, r2) { 520 if(window.matchMedia && nativeMatchMedia) { 521 return matchMedia(query).matches; 522 } else if(window.styleMedia) { 523 return styleMedia.matchMedium(query); 524 } else if(window.media) { 525 return media.matchMedium(query); 526 } else { 527 return prevWinWidth >= r1 && prevWinWidth <= r2; 528 } 529 } 530 531 // range parser 532 function parseRange(rangeStr) { 533 var rangeData = rangeStr.split('..'); 534 var x1 = parseInt(rangeData[0], 10) || -Infinity; 535 var x2 = parseInt(rangeData[1], 10) || Infinity; 536 return [x1, x2].sort(function(a, b){ 537 return a - b; 538 }); 539 } 540 541 // export public functions 542 return { 543 addRange: function(ranges) { 544 // parse data and add items to collection 545 var result = {data:{}}; 546 $.each(ranges, function(property, data){ 547 result.data[property] = { 548 range: parseRange(property), 549 enableCallback: data.on, 550 disableCallback: data.off 551 }; 552 }); 553 handlers.push(result); 554 555 // call resizeHandler to recalculate all events 556 prevWinWidth = null; 557 resizeHandler(); 558 } 559 }; 560}(jQuery)); 561 562/* 563 * Simple Mobile Navigation 564 */ 565; (function ($) { 566 function MobileNav(options) { 567 this.options = $.extend({ 568 container: null, 569 hideOnClickOutside: false, 570 menuActiveClass: 'nav-active', 571 menuOpener: '.nav-opener', 572 menuDrop: '.nav-drop', 573 toggleEvent: 'click', 574 outsideClickEvent: 'click touchstart pointerdown MSPointerDown' 575 }, options); 576 this.initStructure(); 577 this.attachEvents(); 578 } 579 MobileNav.prototype = { 580 initStructure: function () { 581 this.page = $('html'); 582 this.container = $(this.options.container); 583 this.opener = this.container.find(this.options.menuOpener); 584 this.drop = this.container.find(this.options.menuDrop); 585 }, 586 attachEvents: function () { 587 var self = this; 588
589 if (activateResizeHandler) { 590 activateResizeHandler(); 591 activateResizeHandler = null; 592 } 593 594 this.outsideClickHandler = function (e) { 595 if (self.isOpened()) { 596 var target = $(e.target); 597 if (!target.closest(self.opener).length && !target.closest(self.drop).length) { 598 self.hide(); 599 } 600 } 601 }; 602 603 this.openerClickHandler = function (e) { 604 e.preventDefault(); 605 self.toggle(); 606 }; 607 608 this.opener.on(this.options.toggleEvent, this.openerClickHandler); 609 }, 610 isOpened: function () { 611 return this.container.hasClass(this.options.menuActiveClass); 612 }, 613 show: function () { 614 this.container.addClass(this.options.menuActiveClass); 615 if (this.options.hideOnClickOutside) { 616 this.page.on(this.options.outsideClickEvent, this.outsideClickHandler); 617 } 618 }, 619 hide: function () { 620 this.container.removeClass(this.options.menuActiveClass); 621 if (this.options.hideOnClickOutside) { 622 this.page.off(this.options.outsideClickEvent, this.outsideClickHandler); 623 } 624 }, 625 toggle: function () { 626 if (this.isOpened()) { 627 this.hide(); 628 } else { 629 this.show(); 630 } 631 }, 632 destroy: function () { 633 this.container.removeClass(this.options.menuActiveClass); 634 this.opener.off(this.options.toggleEvent, this.clickHandler); 635 this.page.off(this.options.outsideClickEvent, this.outsideClickHandler); 636 } 637 }; 638 639 var activateResizeHandler = function () { 640 var win = $(window), 641 doc = $('html'), 642 resizeClass = 'resize-active', 643 flag, timer; 644 var removeClassHandler = function () { 645 flag = false; 646 doc.removeClass(resizeClass); 647 }; 648 var resizeHandler = function () { 649 if (!flag) { 650 flag = true; 651 doc.addClass(resizeClass); 652 } 653 clearTimeout(timer); 654 timer = setTimeout(removeClassHandler, 500); 655 }; 656 win.on('resize orientationchange', resizeHandler); 657 }; 658 659 $.fn.mobileNav = function (opt) { 660 var args = Array.prototype.slice.call(arguments); 661 var method = args[0]; 662 663 return this.each(function () { 664 var $container = jQuery(this); 665 var instance = $container.data('MobileNav'); 666 667 if (typeof opt === 'object' || typeof opt === 'undefined') { 668 $container.data('MobileNav', new MobileNav($.extend({ 669 container: this 670 }, opt))); 671 } else if (typeof method === 'string' && instance) { 672 if (typeof instance[method] === 'function') { 673 args.shift(); 674 instance[method].apply(instance, args); 675 } 676 } 677 }); 678 }; 679}(jQuery)); 680 681// navigation accesibility module 682function TouchNav(opt) { 683 this.options = { 684 hoverClass: 'hover', 685 menuItems: 'li', 686 menuOpener: 'a', 687 menuDrop: 'ul', 688 navBlock: null, 689 destroy: null 690 }; 691 for (var p in opt) { 692 if (opt.hasOwnProperty(p)) { 693 this.options[p] = opt[p]; 694 } 695 } 696 // console.log(this) 697 this.init(); 698} 699 700 701TouchNav.isActiveOn = function (elem) { 702 return elem && elem.touchNavActive; 703}; 704 705 706TouchNav.prototype = { 707 init: function () { 708 if (typeof this.options.navBlock === 'string') { 709 this.menu = document.getElementById(this.options.navBlock); 710 } else if (typeof this.options.navBlock === 'object') { 711 this.menu = this.options.navBlock; 712 } 713 if (this.menu) { 714 this.hanldeEvents(); 715 } 716 }, 717 hanldeEvents: function () { 718 // attach event handlers 719 var self = this; 720 var touchEvent = (navigator.pointerEnabled && 'pointerdown') || (navigator.msPointerEnabled && 'MSPointerDown') || (this.isTouchDevice && 'touchstart'); 721 this.menuItems = lib.queryElementsBySelector(this.options.menuItems, this.menu); 722 723 var initMenuItem = function (item) { 724 var currentDrop = lib.queryElementsBySelector(self.options.menuDrop, item)[0], 725 currentOpener = lib.queryElementsBySelector(self.options.menuOpener, item)[0]; 726 727 // only for touch input devices 728 if (currentDrop && currentOpener && (self.isTouchDevice || self.isPointerDevice)) { 729 if (!self.options.destroy) { 730 lib.event.add(currentOpener, 'click', lib.bind(self.clickHandler, self)); 731 lib.event.add(currentOpener, 'mousedown', lib.bind(self.mousedownHandler, self)); 732 lib.event.add(currentOpener, touchEvent, function (e) { 733 if (!self.isTouchPointerEvent(e)) { 734 self.preventCurrentClick = false;
735 return; 736 } 737 self.touchFlag = true; 738 self.currentItem = item; 739 self.currentLink = currentOpener; 740 self.pressHandler.apply(self, arguments); 741 }); 742 } else { 743 lib.event.remove(currentOpener, 'click'); 744 lib.event.remove(currentOpener, 'mousedown'); 745 lib.event.remove(currentOpener, touchEvent); 746 } 747 } 748 if (!self.options.destroy) { 749 // for desktop computers and touch devices 750 jQuery(item).bind('mouseenter', function () { 751 if (!self.touchFlag) { 752 self.currentItem = item; 753 self.mouseoverHandler(); 754 } 755 }); 756 jQuery(item).bind('mouseleave', function () { 757 if (!self.touchFlag) { 758 self.currentItem = item; 759 self.mouseoutHandler(); 760 } 761 }); 762 item.touchNavActive = true; 763 764 } else { 765 jQuery(item).unbind('mouseenter'); 766 jQuery(item).unbind('mouseleave'); 767 768 item.touchNavActive = false; 769 770 } 771 772 }; 773 774 // addd handlers for all menu items 775 for (var i = 0; i < this.menuItems.length; i++) { 776 initMenuItem(self.menuItems[i]); 777 } 778 779 // hide dropdowns when clicking outside navigation 780 if (this.isTouchDevice || this.isPointerDevice) { 781 lib.event.add(document.documentElement, 'mousedown', lib.bind(this.clickOutsideHandler, this)); 782 lib.event.add(document.documentElement, touchEvent, lib.bind(this.clickOutsideHandler, this)); 783 } 784 }, 785 mousedownHandler: function (e) { 786 if (this.touchFlag) { 787 e.preventDefault(); 788 this.touchFlag = false; 789 this.preventCurrentClick = false; 790 } 791 }, 792 mouseoverHandler: function () { 793 lib.addClass(this.currentItem, this.options.hoverClass); 794 /**/ 795 // jQuery(this.currentItem).trigger('itemhover'); 796 /**/ 797 }, 798 mouseoutHandler: function () { 799 lib.removeClass(this.currentItem, this.options.hoverClass); 800 /**/ 801 // jQuery(this.currentItem).trigger('itemleave'); 802 /**/ 803 }, 804 hideActiveDropdown: function () { 805 for (var i = 0; i < this.menuItems.length; i++) { 806 if (lib.hasClass(this.menuItems[i], this.options.hoverClass)) { 807 lib.removeClass(this.menuItems[i], this.options.hoverClass); 808 /**/ 809 // jQuery(this.menuItems[i]).trigger('itemleave'); 810 /**/ 811 } 812 } 813 this.activeParent = null; 814 }, 815 pressHandler: function (e) { 816 // hide previous drop (if active) 817 if (this.currentItem !== this.activeParent) { 818 if (this.activeParent && this.currentItem.parentNode === this.activeParent.parentNode) { 819 lib.removeClass(this.activeParent, this.options.hoverClass); 820 } else if (!this.isParent(this.activeParent, this.currentLink)) { 821 this.hideActiveDropdown(); 822 } 823 } 824 // handle current drop 825 this.activeParent = this.currentItem; 826 if (lib.hasClass(this.currentItem, this.options.hoverClass)) { 827 this.preventCurrentClick = false; 828 } else { 829 e.preventDefault(); 830 this.preventCurrentClick = true; 831 lib.addClass(this.currentItem, this.options.hoverClass); 832 /**/ 833 // jQuery(this.currentItem).trigger('itemhover'); 834 /**/ 835 } 836 }, 837 clickHandler: function (e) { 838 // prevent first click on link 839 if (this.preventCurrentClick) { 840 e.preventDefault(); 841 } 842 }, 843 clickOutsideHandler: function (event) { 844 var e = event.changedTouches ? event.changedTouches[0] : event; 845 if (this.activeParent && !this.isParent(this.menu, e.target)) { 846 this.hideActiveDropdown(); 847 this.touchFlag = false;
848 } 849 }, 850 isParent: function (parent, child) { 851 while (child.parentNode) { 852 if (child.parentNode == parent) { 853 return true; 854 } 855 child = child.parentNode; 856 } 857 return false; 858 }, 859 860 isTouchPointerEvent: function (e) { 861 return (e.type.indexOf('touch') > -1) || 862 (navigator.pointerEnabled && e.pointerType === 'touch') || 863 (navigator.msPointerEnabled && e.pointerType == e.MSPOINTER_TYPE_TOUCH); 864 }, 865 isPointerDevice: (function () { 866 return !!(navigator.pointerEnabled || navigator.msPointerEnabled); 867 }()), 868 isTouchDevice: (function () { 869 return !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch); 870 }()) 871}; 872 873/* 874 * Utility module 875 */ 876lib = { 877 hasClass: function(el,cls) { 878 return el && el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false; 879 }, 880 addClass: function(el,cls) { 881 if (el && !this.hasClass(el,cls)) el.className += " "+cls; 882 }, 883 removeClass: function(el,cls) { 884 if (el && this.hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'),' ');} 885 }, 886 extend: function(obj) { 887 for(var i = 1; i < arguments.length; i++) { 888 for(var p in arguments[i]) { 889 if(arguments[i].hasOwnProperty(p)) { 890 obj[p] = arguments[i][p]; 891 } 892 } 893 } 894 return obj; 895 }, 896 each: function(obj, callback) { 897 var property, len; 898 if(typeof obj.length === 'number') { 899 for(property = 0, len = obj.length; property < len; property++) { 900 if(callback.call(obj[property], property, obj[property]) === false) { 901 break; 902 } 903 } 904 } else { 905 for(property in obj) { 906 if(obj.hasOwnProperty(property)) { 907 if(callback.call(obj[property], property, obj[property]) === false) { 908 break; 909 } 910 } 911 } 912 } 913 }, 914 event: (function() { 915 var fixEvent = function(e) { 916 e = e || window.event; 917 if(e.isFixed) return e; else e.isFixed = true; 918 if(!e.target) e.target = e.srcElement; 919 e.preventDefault = e.preventDefault || function() {this.returnValue = false;}; 920 e.stopPropagation = e.stopPropagation || function() {this.cancelBubble = true;}; 921 return e; 922 }; 923 return { 924 add: function(elem, event, handler) { 925 if(!elem.events) { 926 elem.events = {}; 927 elem.handle = function(e) { 928 var ret, handlers = elem.events[e.type]; 929 e = fixEvent(e); 930 for(var i = 0, len = handlers.length; i < len; i++) { 931 if(handlers[i]) { 932 ret = handlers[i].call(elem, e); 933 if(ret === false) { 934 e.preventDefault(); 935 e.stopPropagation(); 936 } 937 } 938 } 939 }; 940 } 941 if(!elem.events[event]) { 942 elem.events[event] = []; 943 if(elem.addEventListener) elem.addEventListener(event, elem.handle, false); 944 else if(elem.attachEvent) elem.attachEvent('on'+event, elem.handle); 945 } 946 elem.events[event].push(handler); 947 }, 948 remove: function(elem, event, handler) { 949 var handlers = elem.events[event]; 950 for(var i = handlers.length - 1; i >= 0; i--) { 951 if(handlers[i] === handler) { 952 handlers.splice(i,1); 953 } 954 } 955 if(!handlers.length) { 956 delete elem.events[event]; 957 if(elem.removeEventListener) elem.removeEventListener(event, elem.handle, false); 958 else if(elem.detachEvent) elem.detachEvent('on'+event, elem.handle); 959 } 960 } 961 }; 962 }()), 963 queryElementsBySelector: function(selector, scope) { 964 scope = scope || document; 965 if(!selector) return []; 966 if(selector === '>*') return scope.children; 967 if(typeof document.querySelectorAll === 'function') { 968 return scope.querySelectorAll(selector); 969 } 970 var selectors = selector.split(','); 971 var resultList = []; 972 for(var s = 0; s < selectors.length; s++) { 973 var currentContext = [scope || document]; 974 var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' '); 975 for (var i = 0; i < tokens.length; i++) { 976 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,''); 977 if (token.indexOf('#') > -1) { 978 var bits = token.split('#'), tagName = bits[0], id = bits[1]; 979 var element = document.getElementById(id); 980 if (element && tagName && element.nodeName.toLowerCase() != tagName) { 981 return []; 982 } 983 currentContext = element ? [element] : []; 984 continue; 985 } 986 if (token.indexOf('.') > -1) { 987 var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0; 988 for (var h = 0; h < currentContext.length; h++) { 989 var elements; 990 if (tagName == '*') { 991 elements = currentContext[h].getElementsByTagName('*'); 992 } else { 993 elements = currentContext[h].getElementsByTagName(tagName); 994 } 995 for (var j = 0; j < elements.length; j++) { 996 found[foundCount++] = elements[j]; 997 } 998 } 999 currentContext = []; 1000 var currentContextIndex = 0; 1001 for (var k = 0; k < found.length; k++) { 1002 if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) { 1003 currentContext[currentContextIndex++] = found[k]; 1004 } 1005 } 1006 continue; 1007 } 1008 if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { 1009 var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4; 1010 if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) { 1011 attrName = 'htmlFor'; 1012 } 1013 var found = [], foundCount = 0; 1014 for (var h = 0; h < currentContext.length; h++) { 1015 var elements; 1016 if (tagName == '*') { 1017 elements = currentContext[h].getElementsByTagName('*'); 1018 } else { 1019 elements = currentContext[h].getElementsByTagName(tagName); 1020 } 1021 for (var j = 0; elements[j]; j++) { 1022 found[foundCount++] = elements[j]; 1023 } 1024 } 1025 currentContext = []; 1026 var currentContextIndex = 0, checkFunction; 1027 switch (attrOperator) { 1028 case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break; 1029 case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break; 1030 case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break; 1031 case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break; 1032 case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break; 1033 case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break; 1034 default : checkFunction = function(e) { return e.getAttribute(attrName) }; 1035 } 1036 currentContext = []; 1037 var currentContextIndex = 0; 1038 for (var k = 0; k < found.length; k++) { 1039 if (checkFunction(found[k])) { 1040 currentContext[currentContextIndex++] = found[k]; 1041 } 1042 } 1043 continue; 1044 } 1045 tagName = token; 1046 var found = [], foundCount = 0; 1047 for (var h = 0; h < currentContext.length; h++) { 1048 var elements = currentContext[h].getElementsByTagName(tagName); 1049 for (var j = 0; j < elements.length; j++) { 1050 found[foundCount++] = elements[j]; 1051 } 1052 } 1053 currentContext = found; 1054 } 1055 resultList = [].concat(resultList,currentContext); 1056 } 1057 return resultList; 1058 }, 1059 trim: function (str) { 1060 return str.replace(/^\s+/, '').replace(/\s+$/, ''); 1061 }, 1062 bind: function(f, scope, forceArgs){ 1063 return function() {return f.apply(scope, typeof forceArgs !== 'undefined' ? [forceArgs] : arguments);}; 1064 } 1065}; 1066 1067/* 1068 * jQuery Open/Close plugin 1069 */ 1070;(function($) { 1071 function OpenClose(options) { 1072 this.options = $.extend({ 1073 addClassBeforeAnimation: true, 1074 hideOnClickOutside: false, 1075 activeClass: 'active', 1076 opener: '.opener', 1077 slider: '.slide', 1078 animSpeed: 400, 1079 effect: 'fade', 1080 event: 'click' 1081 }, options); 1082 this.init(); 1083 } 1084 OpenClose.prototype = { 1085 init: function() { 1086 if (this.options.holder) { 1087 this.findElements(); 1088 this.attachEvents(); 1089 this.makeCallback('onInit', this); 1090 } 1091 }, 1092 findElements: function() { 1093 this.holder = $(this.options.holder); 1094 this.opener = this.holder.find(this.options.opener); 1095 this.slider = this.holder.find(this.options.slider); 1096 }, 1097 attachEvents: function() { 1098 // add handler 1099 var self = this; 1100 this.eventHandler = function(e) { 1101 e.preventDefault(); 1102 if (self.slider.hasClass(slideHiddenClass)) { 1103 self.showSlide(); 1104 } else { 1105 self.hideSlide(); 1106 } 1107 }; 1108 self.slider.attr('aria-hidden', 'true'); 1109 self.opener.attr('aria-expanded', 'false'); 1110 self.opener.on(self.options.event, this.eventHandler); 1111 1112 // hover mode handler 1113 if (self.options.event === 'hover') { 1114 self.opener.on('mouseenter', function() { 1115 if (!self.holder.hasClass(self.options.activeClass)) { 1116 self.showSlide(); 1117 } 1118 }); 1119 self.holder.on('mouseleave', function() { 1120 self.hideSlide(); 1121 }); 1122 } 1123 1124 // outside click handler 1125 self.outsideClickHandler = function(e) { 1126 if (self.options.hideOnClickOutside) { 1127 var target = $(e.target); 1128 if (!target.is(self.holder) && !target.closest(self.holder).length) { 1129 self.hideSlide(); 1130 } 1131 } 1132 }; 1133 1134 // set initial styles 1135 if (this.holder.hasClass(this.options.activeClass)) { 1136 $(document).on('click touchstart', self.outsideClickHandler); 1137 } else { 1138 this.slider.addClass(slideHiddenClass); 1139 } 1140 }, 1141 showSlide: function() { 1142 var self = this; 1143 if (self.options.addClassBeforeAnimation) { 1144 self.holder.addClass(self.options.activeClass); 1145 } 1146 self.slider.attr('aria-hidden', 'false'); 1147 self.opener.attr('aria-expanded', 'true'); 1148 self.slider.removeClass(slideHiddenClass); 1149 $(document).on('click touchstart', self.outsideClickHandler); 1150 1151 self.makeCallback('animStart', true); 1152 toggleEffects[self.options.effect].show({ 1153 box: self.slider, 1154 speed: self.options.animSpeed, 1155 complete: function() { 1156 if (!self.options.addClassBeforeAnimation) { 1157 self.holder.addClass(self.options.activeClass); 1158 } 1159 self.makeCallback('animEnd', true); 1160 } 1161 }); 1162 }, 1163 hideSlide: function() { 1164 var self = this; 1165 if (self.options.addClassBeforeAnimation) { 1166 self.holder.removeClass(self.options.activeClass); 1167 } 1168 $(document).off('click touchstart', self.outsideClickHandler); 1169 1170 self.makeCallback('animStart', false); 1171 toggleEffects[self.options.effect].hide({ 1172 box: self.slider, 1173 speed: self.options.animSpeed, 1174 complete: function() { 1175 if (!self.options.addClassBeforeAnimation) { 1176 self.holder.removeClass(self.options.activeClass); 1177 } 1178 self.slider.addClass(slideHiddenClass); 1179 self.slider.attr('aria-hidden', 'true'); 1180 self.opener.attr('aria-expanded', 'false'); 1181 self.makeCallback('animEnd', false); 1182 } 1183 }); 1184 }, 1185 destroy: function() { 1186 this.slider.removeClass(slideHiddenClass).css({ 1187 display: '' 1188 }); 1189 this.opener.off(this.options.event, this.eventHandler); 1190 this.holder.removeClass(this.options.activeClass).removeData('OpenClose'); 1191 $(document).off('click touchstart', this.outsideClickHandler); 1192 }, 1193 makeCallback: function(name) { 1194 if (typeof this.options[name] === 'function') { 1195 var args = Array.prototype.slice.call(arguments); 1196 args.shift(); 1197 this.options[name].apply(this, args); 1198 } 1199 } 1200 }; 1201 1202 // add stylesheet for slide on DOMReady 1203 var slideHiddenClass = 'js-slide-hidden'; 1204 (function() { 1205 var tabStyleSheet = $('<style type="text/css">')[0]; 1206 var tabStyleRule = '.' + slideHiddenClass; 1207 tabStyleRule += '{position:absolute !important;left:-9999px !important;top:-9999px !important;display:block !important}'; 1208 if (tabStyleSheet.styleSheet) { 1209 tabStyleSheet.styleSheet.cssText = tabStyleRule; 1210 } else { 1211 tabStyleSheet.appendChild(document.createTextNode(tabStyleRule)); 1212 } 1213 $('head').append(tabStyleSheet); 1214 }()); 1215 1216 // animation effects 1217 var toggleEffects = { 1218 slide: { 1219 show: function(o) { 1220 o.box.stop(true).hide().slideDown(o.speed, o.complete); 1221 }, 1222 hide: function(o) {
1223 o.box.stop(true).slideUp(o.speed, o.complete); 1224 } 1225 }, 1226 fade: { 1227 show: function(o) { 1228 o.box.stop(true).hide().fadeIn(o.speed, o.complete); 1229 }, 1230 hide: function(o) { 1231 o.box.stop(true).fadeOut(o.speed, o.complete); 1232 } 1233 }, 1234 none: { 1235 show: function(o) { 1236 o.box.hide().show(0, o.complete); 1237 }, 1238 hide: function(o) { 1239 o.box.hide(0, o.complete); 1240 } 1241 } 1242 }; 1243 1244 // jQuery plugin interface 1245 $.fn.openClose = function(opt) { 1246 var args = Array.prototype.slice.call(arguments); 1247 var method = args[0]; 1248 1249 return this.each(function() { 1250 var $holder = jQuery(this); 1251 var instance = $holder.data('OpenClose'); 1252 1253 if (typeof opt === 'object' || typeof opt === 'undefined') { 1254 $holder.data('OpenClose', new OpenClose($.extend({ 1255 holder: this 1256 }, opt))); 1257 } else if (typeof method === 'string' && instance) { 1258 if (typeof instance[method] === 'function') { 1259 args.shift(); 1260 instance[method].apply(instance, args); 1261 } 1262 } 1263 }); 1264 }; 1265}(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.