1(function ($) { 2 $.fn.StickySidebar = function (options) { 3 var defaults = { 4 'containerSelector': '', 5 'additionalMarginTop': 0, 6 'additionalMarginBottom': 0, 7 'updateSidebarHeight': true, 8 'minWidth': 0, 9 'disableOnResponsiveLayouts': true, 10 'sidebarBehavior': 'modern' 11 }; 12 options = $.extend(defaults, options); 13 14 // Validate options 15 options.additionalMarginTop = parseInt(options.additionalMarginTop) || 0; 16 options.additionalMarginBottom = parseInt(options.additionalMarginBottom) || 0; 17 18 tryInitOrHookIntoEvents(options, this); 19 20 // Try doing init, otherwise hook into window.resize and document.scroll and try again then. 21 function tryInitOrHookIntoEvents(options, $that) { 22 var success = tryInit(options, $that); 23 24 if (!success) { 25 console.log('TST: Body width smaller than options.minWidth. Init is delayed.'); 26 27 $(document).scroll(function (options, $that) { 28 return function (evt) { 29 var success = tryInit(options, $that); 30 31 if (success) { 32 $(this).unbind(evt); 33 } 34 }; 35 }(options, $that)); 36 $(window).resize(function (options, $that) { 37 return function (evt) { 38 var success = tryInit(options, $that); 39 40 if (success) { 41 $(this).unbind(evt); 42 } 43 }; 44 }(options, $that)) 45 } 46 } 47 48 // Try doing init if proper conditions are met. 49 function tryInit(options, $that) { 50 if (options.initialized === true) { 51 return true; 52 } 53 54 if ($('body').width() < options.minWidth) { 55 return false; 56 } 57 58 init(options, $that); 59 60 return true; 61 } 62 63 // Init the sticky sidebar(s). 64 function init(options, $that) { 65 options.initialized = true; 66 67 // Add CSS 68 $('head').append($('<style>.StickySidebar:after {content: ""; display: table; clear: both;}</style>')); 69 70 $that.each(function () { 71 var o = {}; 72 73 o.sidebar = $(this); 74 75 // Save options 76 o.options = options || {}; 77 78 // Get container 79 o.container = $(o.options.containerSelector); 80 if (o.container.length == 0) { 81 o.container = o.sidebar.parent(); 82 } 83 84 // Create sticky sidebar 85 o.sidebar.parents().css('-webkit-transform', 'none'); // Fix for WebKit bug - https://code.google.com/p/chromium/issues/detail?id=20574 86 o.sidebar.css({ 87 'position': 'relative', 88 'overflow': 'visible', 89 // The "box-sizing" must be set to "content-box" because we set a fixed height to this element when the sticky
89sidebar has a fixed position. 90 '-webkit-box-sizing': 'border-box', 91 '-moz-box-sizing': 'border-box', 92 'box-sizing': 'border-box' 93 }); 94 95 // Get the sticky sidebar element. If none has been found, then create one. 96 o.stickySidebar = o.sidebar.find('.StickySidebar'); 97 if (o.stickySidebar.length == 0) { 98 o.sidebar.find('script').remove(); // Remove <script> tags, otherwise they will be run again on the next line. 99 o.stickySidebar = $('<div>').addClass('StickySidebar').append(o.sidebar.children()); 100 o.sidebar.append(o.stickySidebar); 101 } 102 103 // Get existing top and bottom margins and paddings 104 o.marginTop = parseInt(o.sidebar.css('margin-top')); 105 o.marginBottom = parseInt(o.sidebar.css('margin-bottom')); 106 o.paddingTop = parseInt(o.sidebar.css('padding-top')); 107 o.paddingBottom = parseInt(o.sidebar.css('padding-bottom')); 108
109 // Add a temporary padding rule to check for collapsable margins. 110 var collapsedTopHeight = o.stickySidebar.offset().top; 111 var collapsedBottomHeight = o.stickySidebar.outerHeight(); 112 o.stickySidebar.css('padding-top', 1); 113 o.stickySidebar.css('padding-bottom', 1); 114 collapsedTopHeight -= o.stickySidebar.offset().top; 115 collapsedBottomHeight = o.stickySidebar.outerHeight() - collapsedBottomHeight - collapsedTopHeight; 116 if (collapsedTopHeight == 0) { 117 o.stickySidebar.css('padding-top', 0); 118 o.stickySidebarPaddingTop = 0; 119 } 120 else { 121 o.stickySidebarPaddingTop = 1; 122 } 123 124 if (collapsedBottomHeight == 0) { 125 o.stickySidebar.css('padding-bottom', 0); 126 o.stickySidebarPaddingBottom = 0; 127 } 128 else { 129 o.stickySidebarPaddingBottom = 1; 130 } 131 132 // We use this to know whether the user is scrolling up or down. 133 o.previousScrollTop = null; 134 135 // Scroll top (value) when the sidebar has fixed position. 136 o.fixedScrollTop = 0; 137 138 // Set sidebar to default values. 139 resetSidebar(); 140 141 o.onScroll = function (o) { 142 // Stop if the sidebar isn't visible. 143 if (!o.stickySidebar.is(":visible")) { 144 return; 145 } 146 147 // Stop if the window is too small. 148 if ($('body').width() < o.options.minWidth) { 149 resetSidebar(); 150 return; 151 } 152 153 // Stop if the sidebar width is larger than the container width (e.g. the theme is responsive and the sidebar is now below the content) 154 if (o.options.disableOnResponsiveLayouts) { 155 var sidebarWidth = o.sidebar.outerWidth(o.sidebar.css('float') == 'none'); 156 157 if (sidebarWidth + 50 > o.container.width()) { 158 resetSidebar(); 159 return; 160 } 161 } 162 163 var scrollTop = $(document).scrollTop(); 164 var position = 'static'; 165 166 // If the user has scrolled down enough for the sidebar to be clipped at the top, then we can consider changing its position. 167 if (scrollTop >= o.container.offset().top + (o.paddingTop + o.marginTop - o.options.additionalMarginTop)) { 168 // The top and bottom offsets, used in various calculations. 169 var offsetTop = o.paddingTop + o.marginTop + options.additionalMarginTop; 170 var offsetBottom = o.paddingBottom + o.marginBottom + options.additionalMarginBottom; 171 172 // All top and bottom positions are relative to the window, not to the parent elemnts. 173 var containerTop = o.container.offset().top; 174 var containerBottom = o.container.offset().top + getClearedHeight(o.container); 175 176 // The top and bottom offsets relative to the window screen top (zero) and bottom (window height). 177 var windowOffsetTop = 0 + options.additionalMarginTop; 178 var windowOffsetBottom; 179 180 var sidebarSmallerThanWindow = (o.stickySidebar.outerHeight() + offsetTop + offsetBottom) < $(window).height(); 181 if (sidebarSmallerThanWindow) { 182 windowOffsetBottom = windowOffsetTop + o.stickySidebar.outerHeight(); 183 } 184 else { 185 windowOffsetBottom = $(window).height() - o.marginBottom - o.paddingBottom - options.additionalMarginBottom; 186 } 187 188 var staticLimitTop = containerTop - scrollTop + o.paddingTop + o.marginTop; 189 var staticLimitBottom = containerBottom - scrollTop - o.paddingBottom - o.marginBottom; 190 191 var top = o.stickySidebar.offset().top - scrollTop; 192 var scrollTopDiff = o.previousScrollTop - scrollTop; 193 194 // If the sidebar position is fixed, then it won't move up or down by itself. So, we manually adjust the top coordinate. 195 if (o.stickySidebar.css('position') == 'fixed') { 196 if (o.options.sidebarBehavior == 'modern') { 197 top += scrollTopDiff; 198 } 199 } 200 201 if (o.options.sidebarBehavior == 'stick-to-top') { 202 top = options.additionalMarginTop; 203 } 204 205 if (o.options.sidebarBehavior == 'stick-to-bottom') { 206 top = windowOffsetBottom - o.stickySidebar.outerHeight(); 207 } 208 209 if (scrollTopDiff > 0) { // If the user is scrolling up. 210 top = Math.min(top, windowOffsetTop); 211 } 212 else { // If the user is scrolling down. 213 top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight()); 214 } 215 216 top = Math.max(top, staticLimitTop); 217 218 top = Math.min(top, staticLimitBottom - o.stickySidebar.outerHeight()); 219 220 // If the sidebar is the same height as the container, we won't use fixed positioning. 221 var sidebarSameHeightAsContainer = o.container.height() == o.stickySidebar.outerHeight(); 222 223 if (!sidebarSameHeightAsContainer && top == windowOffsetTop) { 224 position = 'fixed'; 225 } 226 else if (!sidebarSameHeightAsContainer && top == windowOffsetBottom - o.stickySidebar.outerHeight()) { 227 position = 'fixed'; 228 } 229 else if (scrollTop + top - o.sidebar.offset().top - o.paddingTop <= options.additionalMarginTop) { 230 // Stuck to the top of the page. No special behavior. 231 position = 'static'; 232 } 233 else { 234 // Stuck to the bottom of the page. 235 position = 'absolute'; 236 } 237 } 238 239 /*
240 * Performance notice: It's OK to set these CSS values at each resize/scroll, even if they don't change. 241 * It's way slower to first check if the values have changed. 242 */ 243 if (position == 'fixed') { 244 o.stickySidebar.css({ 245 'position': 'fixed', 246 'width': o.sidebar.width(), 247 'top': top, 248 'left': o.sidebar.offset().left + parseInt(o.sidebar.css('padding-left')) 249 }); 250 } 251 else if (position == 'absolute') { 252 var css = {}; 253 254 if (o.stickySidebar.css('position') != 'absolute') { 255 css.position = 'absolute'; 256 css.top = scrollTop + top - o.sidebar.offset().top - o.stickySidebarPaddingTop - o.stickySidebarPaddingBottom; 257 } 258 259 css.width = o.sidebar.width(); 260 css.left = ''; 261 262 o.stickySidebar.css(css); 263 } 264 else if (position == 'static') { 265 resetSidebar(); 266 } 267 268 if (position != 'static') { 269 if (o.options.updateSidebarHeight == true) { 270 o.sidebar.css({ 271 'min-height': o.stickySidebar.outerHeight() + o.stickySidebar.offset().top - o.sidebar.offset().top + o.paddingBottom 272 }); 273 } 274 } 275 276 o.previousScrollTop = scrollTop; 277 }; 278 279 // Initialize the sidebar's position. 280 o.onScroll(o); 281 282 // Recalculate the sidebar's position on every scroll and resize. 283 $(document).scroll(function (o) { 284 return function () { 285 o.onScroll(o); 286 }; 287 }(o)); 288 $(window).resize(function (o) { 289 return function () { 290 o.stickySidebar.css({'position': 'static'}); 291 o.onScroll(o); 292 }; 293 }(o)); 294 295 // Reset the sidebar to its default state 296 function resetSidebar() { 297 o.fixedScrollTop = 0; 298 o.sidebar.css({ 299 'min-height': '1px' 300 }); 301 o.stickySidebar.css({ 302 'position': 'static', 303 'width': '' 304 }); 305 } 306 307 // Get the height of a div as if its floated children were cleared. Note that this function fails if the floats are more than one level deep. 308 function getClearedHeight(e) { 309 var height = e.height(); 310 311 e.children().each(function () { 312 height = Math.max(height, $(this).height()); 313 }); 314 315 return height; 316 } 317 }); 318 } 319 } 320})(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.