1(function($) { 2 'use strict'; 3 4 var _destroyFn; 5 6 $.scrollupbar = function($bar, options) { 7 // Default options 8 options = $.extend({ 9 enterViewport: $.noop, 10 fullyEnterViewport: $.noop, 11 exitViewport: $.noop, 12 partiallyExitViewport: $.noop 13 }, options); 14 15 function isFullyInViewport() { 16 return $window.scrollTop() <= $bar.offset().top; 17 } 18 19 function isInViewport() { 20 return $window.scrollTop() < $bar.offset().top + $bar.outerHeight(); 21 } 22 23 var $window = $(window), 24 $document = $(document), 25 minY = $bar.css('position') == 'fixed' ? 0 : $bar.offset().top, 26 lastY = $window.scrollTop(), // Use last Y to detect scroll direction. 27 initialPosTop = $bar.position().top, 28 iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent), 29 timeout; 30 31 $.scrollupbar.isInViewport = isInViewport(); 32 $.scrollupbar.isFullyInViewport = isFullyInViewport(); 33 34 // iOS can't handle momentum scroll properly (See discussion on 35 // http://stackoverflow.com/questions/2863547). 36 if (!iOS) { 37 $window.on('scroll.scrollupbar', function() { 38 var y = $window.scrollTop(), 39 barHeight = $bar.outerHeight(); 40 41 // Ignore elastic scrolling. 42 if (y < 0 || y > ($document.height() - $window.height())) { 43 return; 44 } 45 46 // Cancel the event fired by the previous scroll. 47 if (timeout) { 48 clearTimeout(timeout); 49 } 50 51 if (y < lastY) { // Scrolling up 52 // If the bar is hidden, place it right above the top frame. 53 if (!$.scrollupbar.isInViewport && lastY - barHeight >= minY) { 54 $bar.css('top', lastY - barHeight); 55 $.scrollupbar.isInViewport = true; 56 options.enterViewport(); 57 } 58 59 // Scrolls up bigger than the bar's height fixes the bar on top. 60 if (isFullyInViewport()) { 61 if (y >= minY) { 62 $bar.css({ 63 'position': 'fixed', 64 'top': 0 65 }); 66 } else { 67 $bar.css({ 68 'position': 'absolute', 69 'top': initialPosTop 70 }); 71 } 72 73 if (!$.scrollupbar.isFullyInViewport) { 74 $.scrollupbar.isFullyInViewport = true; 75 options.fullyEnterViewport(); 76 } 77 } 78 79 // Fire an event to reveal the entire bar after 400ms if the scroll 80 // wasn't big enough. 81 timeout = setTimeout(function() { 82 if (!isFullyInViewport()) { 83 $bar.css({ 84 'position': 'fixed', 85 'top': $bar.offset().top - y 86 }); 87 88 $bar.animate({'top': 0}, 100, function() { 89 $.scrollupbar.isFullyInViewport = true; 90 options.fullyEnterViewport(); 91 }); 92 } 93 }, 400); 94 } else if (y > lastY) { // Scrolling down 95 // Unfix the bar allowing it to scroll with the page. 96 if ($.scrollupbar.isFullyInViewport) { 97 $bar.css({ 98 'position': 'absolute', 99 'top': lastY > minY ? lastY : initialPosTop 100 }); 101 102 if (!isFullyInViewport()) { 103 $.scrollupbar.isFullyInViewport = false; 104 options.partiallyExitViewport(); 105 } 106 } 107 108 if ($.scrollupbar.isInViewport && !isInViewport()) { 109 $.scrollupbar.isInViewport = false; 110 options.exitViewport(); 111 } 112 113 // Fire an event to hide the entire bar after 400ms if the scroll 114 // wasn't big enough. 115 timeout = setTimeout(function() { 116 if (isInViewport() && y - barHeight >= minY) { 117 $bar.animate({'top': y - barHeight}, 100, function() { 118 $.scrollupbar.isInViewport = false; 119 options.exitViewport(); 120 }); 121 } 122 }, 400); 123 } 124 125 lastY = y; 126 127 if (isInViewport() || isFullyInViewport()) $("#topbar2").css("display", "none"); 128 else $("#topbar2").css("display", "block"); 129 130 }); 131 } else { // Fallback simplified behaviour for iOS. 132 $window.on('touchstart.scrollupbar', function () { 133 lastY = $window.scrollTop(); 134 }); 135 136 $window.on('touchend.scrollupbar', function () { 137 var y = $window.scrollTop(); 138 139 if (y < lastY || y - $bar.outerHeight() < minY) { // Scrolling up 140 if (y <= minY) { 141 // Restore original position. 142 $bar.css({ 143 'position': 'absolute', 144 'top': initialPosTop 145 }); 146
147 $bar.show(function() { 148 $.scrollupbar.isInViewport = true; 149 $.scrollupbar.isFullyInViewport = true; 150 options.enterViewport(); 151 options.fullyEnterViewport(); 152 }); 153 } else { 154 $bar.css({ 155 'position': 'fixed', 156 'top': 0 157 }); 158 159 $.scrollupbar.isInViewport = true; 160 options.enterViewport(); 161 162 $bar.slideDown(function() { 163 $.scrollupbar.isFullyInViewport = true; 164 options.fullyEnterViewport(); 165 }); 166 } 167 } else if (y > lastY) { // Scrolling down 168 $.scrollupbar.isFullyInViewport = false; 169 options.partiallyExitViewport(); 170 171 $bar.slideUp(function() { 172 $.scrollupbar.isInViewport = false; 173 options.exitViewport(); 174 }); 175 } 176 177 lastY = y; 178 179 if (isInViewport() || isFullyInViewport()) $("#topbar2").css("display", "none"); 180 else $("#topbar2").css("display", "block"); 181 182 }); 183 } 184 185 _destroyFn = function() { 186 // Unbind all listeners added by scrollupbar plugin 187 $window.off('.scrollupbar'); 188 189 // Restore original bar position. 190 $bar.css({ 191 'position': 'absolute', 192 'top': initialPosTop 193 }); 194 }; 195 196 return $bar; 197 }; 198 199 $.scrollupbar.destroy = function() { 200 if (_destroyFn) { 201 return _destroyFn(); 202 } 203 }; 204 205 $.fn.scrollupbar = function(options) { 206 return $.scrollupbar(this, options); 207 }; 208})(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.