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