1/** 2 * Extend jquery with a scrollspy plugin. 3 * This watches the window scroll and fires events when elements are scrolled into viewport. 4 * 5 * throttle() and getTime() taken from Underscore.js 6 * https://github.com/jashkenas/underscore 7 * 8 * @author Copyright 2013 John Smart 9 * @license https://raw.github.com/thesmart/jquery-scrollspy/master/LICENSE 10 * @see https://github.com/thesmart 11 * @version 0.1.2 12 */ 13(function($) { 14 15 var jWindow = $(window); 16 var elements = []; 17 var elementsInView = []; 18 var isSpying = false; 19 var ticks = 0; 20 var offset = { 21 top : 0, 22 right : 0, 23 bottom : 0, 24 left : 0, 25 } 26 27 /** 28 * Find elements that are within the boundary 29 * @param {number} top 30 * @param {number} right 31 * @param {number} bottom 32 * @param {number} left 33 * @return {jQuery} A collection of elements 34 */ 35 function findElements(top, right, bottom, left) { 36 var hits = $(); 37 $.each(elements, function(i, element) { 38 var elTop = element.offset().top, 39 elLeft = element.offset().left, 40 elRight = elLeft + element.width(), 41 elBottom = elTop + element.height(); 42 43 var isIntersect = !(elLeft > right || 44 elRight < left || 45 elTop > bottom || 46 elBottom < top); 47 48 if (isIntersect) { 49 hits.push(element); 50 } 51 }); 52 53 return hits; 54 } 55 56 /** 57 * Called when the user scrolls the window 58 */ 59 function onScroll() { 60 // unique tick id 61 ++ticks; 62 63 // viewport rectangle 64 var top = jWindow.scrollTop(), 65 left = jWindow.scrollLeft(), 66 right = left + jWindow.width(), 67 bottom = top + jWindow.height(); 68 69 // determine which elements are in view 70 var intersections = findElements(top+offset.top, right+offset.right, bottom+offset.bottom, left+offset.left); 71 $.each(intersections, function(i, element) { 72 var lastTick = element.data('scrollSpy:ticks'); 73 if (typeof lastTick != 'number') { 74 // entered into view 75 element.triggerHandler('scrollSpy:enter'); 76 } 77 78 // update tick id 79 element.data('scrollSpy:ticks', ticks); 80 }); 81 82 // determine which elements are no longer in view 83 $.each(elementsInView, function(i, element) { 84 var lastTick = element.data('scrollSpy:ticks'); 85 if (typeof lastTick == 'number' && lastTick !== ticks) { 86 // exited from view 87 element.triggerHandler('scrollSpy:exit'); 88 element.data('scrollSpy:ticks', null); 89 } 90 }); 91 92 // remember elements in view for next tick 93 elementsInView = intersections; 94 } 95 96 /** 97 * Called when window is resized 98 */ 99 function onWinSize() { 100 jWindow.trigger('scrollSpy:winSize'); 101 } 102 103 /** 104 * Get time in ms 105 * @license https://raw.github.com/jashkenas/underscore/master/LICENSE 106 * @type {function} 107 * @return {number} 108 */ 109 var getTime = (Date.now || function () { 110 return new Date().getTime(); 111 }); 112 113 /** 114 * Returns a function, that, when invoked, will only be triggered at most once 115 * during a given window of time. Normally, the throttled function will run 116 * as much as it can, without ever going more than once per `wait` duration; 117 * but if you'd like to disable the execution on the leading edge, pass 118 * `{leading: false}`. To disable execution on the trailing edge, ditto. 119 * @license https://raw.github.com/jashkenas/underscore/master/LICENSE 120 * @param {function} func 121 * @param {number} wait 122 * @param {Object=} options 123 * @returns {Function} 124 */ 125 function throttle(func, wait, options) { 126 var context, args, result; 127 var timeout = null; 128 var previous = 0; 129 options || (options = {}); 130 var later = function () { 131 previous = options.leading === false ? 0 : getTime(); 132 timeout = null; 133 result = func.apply(context, args); 134 context = args = null; 135 }; 136 return function () { 137 var now = getTime(); 138 if (!previous && options.leading === false) previous = now; 139 var remaining = wait - (now - previous); 140 context = this; 141 args = arguments; 142 if (remaining <= 0) { 143 clearTimeout(timeout); 144 timeout = null; 145 previous = now; 146 result = func.apply(context, args); 147 context = args = null; 148 } else if (!timeout && options.trailing !== false) { 149 timeout = setTimeout(later, remaining); 150 } 151 return result; 152 }; 153 }; 154 155 /** 156 * Enables ScrollSpy using a selector 157 * @param {jQuery|string} selector The elements collection, or a selector 158 * @param {Object=} options Optional. 159 throttle : number -> scrollspy throttling. Default: 100 ms 160 offsetTop : number -> offset from top. Default: 0 161 offsetRight : number -> offset from right. Default: 0 162 offsetBottom : number -> offset from bottom. Default: 0 163 offsetLeft : number -> offset from left. Default: 0 164 * @returns {jQuery} 165 */ 166 $.scrollSpy = function(selector, options) { 167 selector = $(selector); 168 selector.each(function(i, element) { 169 elements.push($(element)); 170 }); 171 options = options || { 172 throttle: 100 173 }; 174 175 offset.top = options.offsetTop || 0; 176 offset.right = options.offsetRight || 0; 177 offset.bottom = options.offsetBottom || 0; 178 offset.left = options.offsetLeft || 0; 179 180 var throttledScroll = throttle(onScroll, options.throttle || 100); 181 var readyScroll = function(){ 182 $(document).ready(throttledScroll); 183 }; 184 185 if (!isSpying) { 186 jWindow.on('scroll', readyScroll); 187 jWindow.on('resize', readyScroll);
188 isSpying = true; 189 } 190 191 // perform a scan once, after current execution context, and after dom is ready 192 setTimeout(readyScroll, 0); 193 194 return selector; 195 }; 196 197 /** 198 * Listen for window resize events 199 * @param {Object=} options Optional. Set { throttle: number } to change throttling. Default: 100 ms 200 * @returns {jQuery} $(window) 201 */ 202 $.winSizeSpy = function(options) { 203 $.winSizeSpy = function() { return jWindow; }; // lock from multiple calls 204 options = options || { 205 throttle: 100 206 }; 207 return jWindow.on('resize', throttle(onWinSize, options.throttle || 100)); 208 }; 209 210 /** 211 * Enables ScrollSpy on a collection of elements 212 * e.g. $('.scrollSpy').scrollSpy() 213 * @param {Object=} options Optional. 214 throttle : number -> scrollspy throttling. Default: 100 ms 215 offsetTop : number -> offset from top. Default: 0 216 offsetRight : number -> offset from right. Default: 0 217 offsetBottom : number -> offset from bottom. Default: 0 218 offsetLeft : number -> offset from left. Default: 0 219 * @returns {jQuery} 220 */ 221 $.fn.scrollSpy = function(options) { 222 return $.scrollSpy($(this), options); 223 }; 224 225})(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.