1/** 2 * RB Viewport Observer v2.3 3 * High-performance viewport detection using IntersectionObserver 4 * 5 * @package foxiz 6 * @version 2.3.0 7 */ 8var RB_VIEWPORT_OBSERVER = (function (existing) { 9 'use strict'; 10 11 if (existing && existing._initialized && existing._version === '2.3') { 12 return existing; 13 } 14 15 var hasIO = 'IntersectionObserver' in window; 16 var observers = {}; 17 var count = 0; 18 var lastY = 0; 19 var dir = 'down'; 20 var listening = false; 21 var rafId = 0; 22 var idNum = 0; 23 var win = window; 24 var doc = document; 25 26 function getScrollY() { 27 return win.pageYOffset || 0; 28 } 29 30 function onScroll() { 31 if (!rafId) { 32 rafId = requestAnimationFrame(function () { 33 var y = getScrollY(); 34 var diff = y - lastY; 35 if (diff > 5) dir = 'down'; 36 else if (diff < -5) dir = 'up'; 37 lastY = y; 38 rafId = 0; 39 }); 40 } 41 } 42 43 function startListening() { 44 if (!listening && count > 0) { 45 lastY = getScrollY(); 46 win.addEventListener('scroll', onScroll, { passive: true }); 47 listening = true; 48 } 49 } 50 51 function stopListening() { 52 if (listening && count === 0) { 53 win.removeEventListener('scroll', onScroll); 54 listening = false; 55 if (rafId) { 56 cancelAnimationFrame(rafId); 57 rafId = 0; 58 } 59 } 60 } 61 62 function getEl(el) { 63 return !el ? null : (el.nodeType ? el : (el[0] || null)); 64 } 65 66 function parseOffset(offset, el) { 67 if (typeof offset === 'function') offset = offset.call(el); 68 69 if (typeof offset === 'number') { 70 return { m: '0px 0px ' + offset + 'px 0px', v: offset, p: false }; 71 } 72 73 if (typeof offset === 'string' && offset.indexOf('%') > -1) { 74 var n = parseInt(offset, 10); 75 var v = n > 100 ? n - 100 : (n >= 0 ? -(100 - n) : n); 76 return { m: '0px 0px ' + v + '% 0px', v: v, p: true }; 77 } 78
79 return { m: '0px', v: 0, p: false }; 80 } 81 82 function makeCallback(id) { 83 return function (entries) { 84 var d = observers[id]; 85 if (!d) return; 86 87 var e = entries[0]; 88 var was = d.inView; 89 var now = e.isIntersecting; 90 91 if (was === now) return; 92 d.inView = now; 93 94 var direction = now ? dir : (dir === 'down' ? 'up' : 'down'); 95 96 // Throttle continuous mode 97 if (d.cont && now) { 98 var t = Date.now(); 99 if (t - d.lastT < 100) return; 100 d.lastT = t; 101 } 102 103 if (d.fn) { 104 d.fn.call(d.ctx, direction); 105 } 106 107 if (d.once && direction === 'down') { 108 unobserve(id); 109 } 110 }; 111 } 112 113 function makeContext(id, el) { 114 return { 115 element: el, 116 destroy: function () { unobserve(id); }, 117 reset: function () { resetState(id); } 118 }; 119 } 120 121 function observe(id, opts) { 122 if (!hasIO || !opts || !opts.element) return null; 123 124 var el = getEl(opts.element); 125 if (!el) return null; 126 127 if (!id) id = 'vo_' + (++idNum); 128 if (observers[id]) unobserve(id); 129 130 var p = parseOffset(opts.offset, el); 131 var cb = makeCallback(id); 132 var ctx = makeContext(id, opts.element); 133 134 var io = new IntersectionObserver(cb, { 135 root: null, 136 rootMargin: p.m, 137 threshold: 0 138 }); 139 140 observers[id] = { 141 io: io, 142 el: el, 143 fn: opts.handler, 144 ctx: ctx, 145 off: opts.offset, 146 once: !!opts.once, 147 cont: !!opts.continuous, 148 m: p.m, 149 val: p.v, 150 pct: p.p, 151 inView: false, 152 lastT: 0 153 }; 154 155 count++; 156 startListening(); 157 io.observe(el); 158 159 return id; 160 } 161 162 function unobserve(id) { 163 var d = observers[id]; 164 if (d) { 165 d.io.disconnect(); 166 d.io = d.el = d.fn = d.ctx = null; 167 delete observers[id]; 168 count--; 169 stopListening(); 170 } 171 } 172 173 function resetState(id) { 174 var d = observers[id]; 175 if (d) { 176 d.inView = false; 177 d.lastT = 0; 178 } 179 } 180 181 function refresh(id) { 182 var d = observers[id]; 183 if (!d) return; 184 185 if (!doc.body.contains(d.el)) { 186 unobserve(id); 187 return; 188 } 189 190 d.io.disconnect(); 191 192 var p = parseOffset(d.off, d.el); 193 d.m = p.m; 194 d.val = p.v; 195 d.pct = p.p; 196 197 var cb = makeCallback(id); 198 var io = new IntersectionObserver(cb, { 199 root: null, 200 rootMargin: p.m, 201 threshold: 0 202 }); 203 204 io.observe(d.el); 205 d.io = io; 206 d.inView = false; 207 d.lastT = 0; 208 } 209 210 function refreshAll() { 211 var ids = Object.keys(observers); 212 for (var i = 0, len = ids.length; i < len; i++) { 213 refresh(ids[i]); 214 } 215 } 216 217 function has(id) { 218 return id in observers; 219 } 220 221 function getActiveIds() { 222 return Object.keys(observers); 223 } 224 225 function destroyAll() { 226 var ids = Object.keys(observers); 227 for (var i = ids.length - 1; i >= 0; i--) { 228 unobserve(ids[i]); 229 } 230 } 231 232 function getDir() { 233 return dir; 234 } 235 236 return { 237 _initialized: true, 238 _version: '2.3', 239 observe: observe, 240 unobserve: unobserve, 241 destroy: unobserve, 242 refresh: refresh, 243 refreshAll: refreshAll, 244 resetState: resetState, 245 has: has, 246 getActiveIds: getActiveIds, 247 destroyAll: destroyAll, 248 getScrollDirection: getDir 249 }; 250 251})(typeof RB_VIEWPORT_OBSERVER !== 'undefined' ? RB_VIEWPORT_OBSERVER : undefined);
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.