1/*! 2 * Theia Sticky Sidebar v1.7.0 (Modified by ThemeSphere) 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-2016 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': 0, 19 'disableOnResponsiveLayouts': true, 20 'sidebarBehavior': 'modern', 21 'defaultPosition': 'relative', 22 'namespace': 'TSS' 23 }; 24 25 options = $.extend(defaults, options); 26 27 // Validate options 28 options.additionalMarginTop = parseInt(options.additionalMarginTop) || 0; 29 options.additionalMarginBottom = parseInt(options.additionalMarginBottom) || 0; 30 31 tryInitOrHookIntoEvents(options, this); 32 33 // Try doing init, otherwise hook into window.resize and document.scroll and try again then. 34 function tryInitOrHookIntoEvents(options, $that) { 35 var success = tryInit(options, $that); 36 37 if (!success) {
38 console.log('TSS: Body width smaller than options.minWidth. Init is delayed.'); 39 40 $(document).on('scroll.' + options.namespace, function (options, $that) { 41 return function (evt) { 42 var success = tryInit(options, $that); 43 44 if (success) { 45 $(this).unbind(evt); 46 } 47 }; 48 }(options, $that)); 49 $(window).on('resize.' + options.namespace, function (options, $that) { 50 return function (evt) { 51 var success = tryInit(options, $that); 52 53 if (success) { 54 $(this).unbind(evt); 55 } 56 }; 57 }(options, $that)) 58 } 59 } 60 61 // Try doing init if proper conditions are met. 62 function tryInit(options, $that) { 63 if (options.initialized === true) { 64 return true; 65 } 66 67 if ($('body').width() < options.minWidth) { 68 return false; 69 } 70 71 init(options, $that); 72 73 return true; 74 } 75 76 // Init the sticky sidebar(s). 77 function init(options, $that) { 78 options.initialized = true; 79 80 // Add CSS 81 var existingStylesheet = $('#theia-sticky-sidebar-stylesheet-' + options.namespace); 82 if (existingStylesheet.length === 0) { 83 $('head').append($('<style id="theia-sticky-sidebar-stylesheet-' + options.namespace + '">.theiaStickySidebar:after {content: ""; display: table; clear: both;}</style>')); 84 } 85 86 $that.each(function () { 87 var o = {}; 88 89 o.sidebar = $(this); 90 91 // Save options 92 o.options = options || {}; 93 94 // +EDIT: Options editable. 95 o.sidebar.data('tss-options', o.options); 96 97 // Get container 98 o.container = $(o.options.containerSelector); 99 if (o.container.length == 0) { 100 o.container = o.sidebar.parent(); 101 } 102 103 // Create sticky sidebar 104 o.sidebar.parents().css('-webkit-transform', 'none'); // Fix for WebKit bug - https://code.google.com/p/chromium/issues/detail?id=20574 105 o.sidebar.css({ 106 'position': o.options.defaultPosition, 107 'overflow': 'visible', 108 // The "box-sizing" must be set to "content-box" because we set a fixed height to this element when the sticky
108sidebar has a fixed position. 109 '-webkit-box-sizing': 'border-box', 110 '-moz-box-sizing': 'border-box', 111 'box-sizing': 'border-box' 112 }); 113 114 // Get the sticky sidebar element. If none has been found, then create one. 115 o.stickySidebar = o.sidebar.find('.theiaStickySidebar'); 116 if (o.stickySidebar.length == 0) { 117 // Remove <script> tags, otherwise they will be run again when added to the stickySidebar. 118 var javaScriptMIMETypes = /(?:text|application)\/(?:x-)?(?:javascript|ecmascript)/i; 119 o.sidebar.find('script').filter(function (index, script) { 120 return script.type.length === 0 || script.type.match(javaScriptMIMETypes); 121 }).remove(); 122 123 o.stickySidebar = $('<div>').addClass('theiaStickySidebar').append(o.sidebar.children()); 124 o.sidebar.append(o.stickySidebar); 125 } 126 127 // Get existing top and bottom margins and paddings 128 o.marginBottom = parseInt(o.sidebar.css('margin-bottom')); 129 o.paddingTop = parseInt(o.sidebar.css('padding-top')); 130 o.paddingBottom = parseInt(o.sidebar.css('padding-bottom')); 131
132 // Add a temporary padding rule to check for collapsable margins. 133 var collapsedTopHeight = o.stickySidebar.offset().top; 134 var collapsedBottomHeight = o.stickySidebar.outerHeight(); 135 o.stickySidebar.css('padding-top', 1); 136 o.stickySidebar.css('padding-bottom', 1); 137 collapsedTopHeight -= o.stickySidebar.offset().top; 138 collapsedBottomHeight = o.stickySidebar.outerHeight() - collapsedBottomHeight - collapsedTopHeight; 139 if (collapsedTopHeight == 0) { 140 o.stickySidebar.css('padding-top', 0); 141 o.stickySidebarPaddingTop = 0; 142 } 143 else { 144 o.stickySidebarPaddingTop = 1; 145 } 146 147 if (collapsedBottomHeight == 0) { 148 o.stickySidebar.css('padding-bottom', 0); 149 o.stickySidebarPaddingBottom = 0; 150 } 151 else { 152 o.stickySidebarPaddingBottom = 1; 153 } 154 155 // We use this to know whether the user is scrolling up or down. 156 o.previousScrollTop = null; 157 158 // Scroll top (value) when the sidebar has fixed position. 159 o.fixedScrollTop = 0; 160 161 // Set sidebar to default values. 162 resetSidebar(); 163 164 o.onScroll = function (o) { 165 // Stop if the sidebar isn't visible. 166 if (!o.stickySidebar.is(":visible")) { 167 return; 168 } 169 170 // Stop if the window is too small. 171 if ($('body').width() < o.options.minWidth) { 172 resetSidebar(); 173 return; 174 } 175 176 // 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) 177 if (o.options.disableOnResponsiveLayouts) { 178 var sidebarWidth = o.sidebar.outerWidth(o.sidebar.css('float') == 'none'); 179 180 if (sidebarWidth + 50 > o.container.width()) { 181 resetSidebar(); 182 return; 183 } 184 } 185 186 var scrollTop = $(document).scrollTop(); 187 var position = 'static'; 188 189 // If the user has scrolled down enough for the sidebar to be clipped at the top, then we can consider changing its position. 190 if (scrollTop >= o.sidebar.offset().top + (o.paddingTop - o.options.additionalMarginTop)) { 191 // The top and bottom offsets, used in various calculations. 192 var offsetTop = o.paddingTop + options.additionalMarginTop; 193 var offsetBottom = o.paddingBottom + o.marginBottom + options.additionalMarginBottom; 194 195 // All top and bottom positions are relative to the window, not to the parent elemnts. 196 var containerTop = o.sidebar.offset().top; 197 var containerBottom = o.sidebar.offset().top + getClearedHeight(o.container); 198 199 // The top and bottom offsets relative to the window screen top (zero) and bottom (window height). 200 var windowOffsetTop = 0 + options.additionalMarginTop; 201 var windowOffsetBottom; 202 203 var sidebarSmallerThanWindow = (o.stickySidebar.outerHeight() + offsetTop + offsetBottom) < $(window).height(); 204 if (sidebarSmallerThanWindow) { 205 windowOffsetBottom = windowOffsetTop + o.stickySidebar.outerHeight(); 206 } 207 else { 208 windowOffsetBottom = $(window).height() - o.marginBottom - o.paddingBottom - options.additionalMarginBottom; 209 } 210 211 var staticLimitTop = containerTop - scrollTop + o.paddingTop; 212 var staticLimitBottom = containerBottom - scrollTop - o.paddingBottom - o.marginBottom; 213 214 var top = o.stickySidebar.offset().top - scrollTop; 215 var scrollTopDiff = o.previousScrollTop - scrollTop; 216 217 // If the sidebar position is fixed, then it won't move up or down by itself. So, we manually adjust the top coordinate. 218 if (o.stickySidebar.css('position') == 'fixed') { 219 if (o.options.sidebarBehavior == 'modern') { 220 top += scrollTopDiff; 221 } 222 } 223 224 if (o.options.sidebarBehavior == 'stick-to-top') { 225 top = options.additionalMarginTop; 226 } 227 228 if (o.options.sidebarBehavior == 'stick-to-bottom') { 229 top = windowOffsetBottom - o.stickySidebar.outerHeight(); 230 } 231 232 if (scrollTopDiff > 0) { // If the user is scrolling up. 233 top = Math.min(top, windowOffsetTop); 234 } 235 else { // If the user is scrolling down. 236 top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight()); 237 } 238 239 top = Math.max(top, staticLimitTop); 240 241 top = Math.min(top, staticLimitBottom - o.stickySidebar.outerHeight()); 242 243 // If the sidebar is the same height as the container, we won't use fixed positioning. 244 var sidebarSameHeightAsContainer = o.container.height() == o.stickySidebar.outerHeight(); 245 246 if (!sidebarSameHeightAsContainer && top == windowOffsetTop) { 247 position = 'fixed'; 248 } 249 else if (!sidebarSameHeightAsContainer && top == windowOffsetBottom - o.stickySidebar.outerHeight()) { 250 position = 'fixed'; 251 } 252 else if (scrollTop + top - o.sidebar.offset().top - o.paddingTop <= options.additionalMarginTop) { 253 // Stuck to the top of the page. No special behavior. 254 position = 'static'; 255 } 256 else { 257 // Stuck to the bottom of the page. 258 position = 'absolute'; 259 } 260 } 261 262 // +EDIT: Subpixel fix. 263 const transY = window.devicePixelRatio < 1.6 ? Math.round : y => y; 264 265 /*
266 * Performance notice: It's OK to set these CSS values at each resize/scroll, even if they don't change. 267 * It's way slower to first check if the values have changed. 268 */ 269 if (position == 'fixed') { 270 var scrollLeft = $(document).scrollLeft(); 271 272 o.stickySidebar.css({ 273 'position': 'fixed', 274 'width': getWidthForObject(o.stickySidebar) + 'px', 275 // +EDIT: Subpixel fix. 276 'transform': 'translateY(' + transY(top) + 'px)', 277 'left': (o.sidebar.offset().left + parseInt(o.sidebar.css('padding-left')) + parseInt(o.sidebar.css('border-left')) - scrollLeft) + 'px', 278 'top': '0px' 279 }); 280 } 281 else if (position == 'absolute') { 282 var css = {}; 283 284 if (o.stickySidebar.css('position') != 'absolute') { 285 css.position = 'absolute'; 286 // +EDIT: Subpixel fix. 287 css.transform = 'translateY(' + transY(scrollTop + top - o.sidebar.offset().top - o.stickySidebarPaddingTop - o.stickySidebarPaddingBottom) + 'px)'; 288 css.top = '0px'; 289 } 290 291 css.width = getWidthForObject(o.stickySidebar) + 'px'; 292 css.left = ''; 293 294 o.stickySidebar.css(css); 295 } 296 else if (position == 'static') { 297 resetSidebar(); 298 } 299 300 if (position != 'static') { 301 if (o.options.updateSidebarHeight == true) { 302 o.sidebar.css({ 303 'min-height': o.stickySidebar.outerHeight() + o.stickySidebar.offset().top - o.sidebar.offset().top + o.paddingBottom 304 }); 305 } 306 } 307 308 o.previousScrollTop = scrollTop; 309 }; 310 311 // Initialize the sidebar's position. 312 o.onScroll(o); 313 314 // Recalculate the sidebar's position on every scroll and resize. 315 $(document).on('scroll.' + o.options.namespace, function (o) { 316 return function () { 317 o.onScroll(o); 318 }; 319 }(o)); 320 $(window).on('resize.' + o.options.namespace, function (o) { 321 return function () { 322 o.stickySidebar.css({'position': 'static'}); 323 o.onScroll(o); 324 }; 325 }(o)); 326 327 // Recalculate the sidebar's position every time the sidebar changes its size. 328 if (typeof ResizeSensor !== 'undefined') { 329 new ResizeSensor(o.stickySidebar[0], function (o) { 330 return function () { 331 o.onScroll(o); 332 }; 333 }(o)); 334 } 335 336 // Reset the sidebar to its default state 337 function resetSidebar() { 338 o.fixedScrollTop = 0; 339 o.sidebar.css({ 340 'min-height': '1px' 341 }); 342 o.stickySidebar.css({ 343 'position': 'static', 344 'width': '', 345 'transform': 'none' 346 }); 347 } 348 349 // 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. 350 function getClearedHeight(e) { 351 var height = e.height(); 352 353 e.children().each(function () { 354 height = Math.max(height, $(this).height()); 355 }); 356 357 return height; 358 } 359 }); 360 } 361 362 function getWidthForObject(object) { 363 var width; 364 365 try { 366 width = object[0].getBoundingClientRect().width; 367 } 368 catch (err) { 369 } 370 371 if (typeof width === "undefined") { 372 width = object.width(); 373 } 374 375 return width; 376 } 377 378 return this; 379 } 380})(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.