/*! * # Fomantic-UI 2.9.3 - Visibility * https://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * https://opensource.org/licenses/MIT * */ (function ($, window, document) { 'use strict'; function isFunction(obj) { return typeof obj === 'function' && typeof obj.nodeType !== 'number'; } window = window !== undefined && window.Math === Math ? window : globalThis; $.fn.visibility = function (parameters) { var $allModules = $(this), time = Date.now(), performance = [], query = arguments[0], methodInvoked = typeof query === 'string', queryArguments = [].slice.call(arguments, 1), contextCheck = function (context, win) { var $context; if ([window, document].indexOf(context) >= 0) { $context = $(context); } else { $context = $(win.document).find(context); if ($context.length === 0) { $context = win.frameElement ? contextCheck(context, win.parent) : window; } } return $context; }, returnedValue, moduleCount = $allModules.length, loadedCount = 0 ; $allModules.each(function () { var settings = $.isPlainObject(parameters) ? $.extend(true, {}, $.fn.visibility.settings, parameters) : $.extend({}, $.fn.visibility.settings), className = settings.className, namespace = settings.namespace, error = settings.error, metadata = settings.metadata, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $window = $(window), $module = $(this), $context = contextCheck(settings.context, window), $placeholder, instance = $module.data(moduleNamespace), element = this, disabled = false, contextObserver, observer, module ; module = { initialize: function () { module.debug('Initializing', settings); module.setup.cache(); if (module.should.trackChanges()) { if (settings.type === 'image') { module.setup.image(); } if (settings.type === 'fixed') { module.setup.fixed(); } if (settings.observeChanges) { module.observeChanges(); } module.bind.events(); } module.save.position(); if (!module.is.visible()) { module.error(error.visible, $module); } if (settings.initialCheck) { module.checkVisibility(); } module.instantiate(); }, instantiate: function () { module.debug('Storing instance', module); $module .data(moduleNamespace, module) ; instance = module; }, destroy: function () { module.verbose('Destroying previous module'); if (observer) { observer.disconnect(); } if (contextObserver) { contextObserver.disconnect(); } $window .off('load' + eventNamespace, module.event.load) .off('resize' + eventNamespace, module.event.resize) ; $context .off('scroll' + eventNamespace, module.event.scroll) .off('scrollchange' + eventNamespace, module.event.scrollchange) ; if (settings.type === 'fixed') { module.resetFixed(); module.remove.placeholder(); } $module .off(eventNamespace) .removeData(moduleNamespace) ; }, observeChanges: function () { if ('MutationObserver' in window) { contextObserver = new MutationObserver(module.event.contextChanged); observer = new MutationObserver(module.event.changed); contextObserver.observe(document, { childList: true, subtree: true, }); observer.observe(element, { childList: true, subtree: true, }); module.debug('Setting up mutation observer', observer); } }, bind: { events: function () { module.verbose('Binding visibility events to scroll and resize'); if (settings.refreshOnLoad) { $window .on('load' + eventNamespace, module.event.load) ; } $window .on('resize' + eventNamespace, module.event.resize) ; // pub/sub pattern $context .off('scroll' + eventNamespace) .on('scroll' + eventNamespace, module.event.scroll) .on('scrollchange' + eventNamespace, module.event.scrollchange) ; }, }, event: { changed: function (mutations) { module.verbose('DOM tree modified, updating visibility calculations'); module.timer = setTimeout(function () { module.verbose('DOM tree modified, updating sticky menu'); module.refresh(); }, 100); }, contextChanged: function (mutations) { [].forEach.call(mutations, function (mutation) { if (mutation.removedNodes) { [].forEach.call(mutation.removedNodes, function (node) { if (node === element || $(node).find(element).length > 0) { module.debug('Element removed from DOM, tearing down events'); module.destroy(); } }); } }); }, resize: function () { module.debug('Window resized'); if (settings.refreshOnResize) { requestAnimationFrame(module.refresh); } }, load: function () { module.debug('Page finished loading'); requestAnimationFrame(module.refresh); }, // publishes scrollchange event on one scroll scroll: function () { if (settings.throttle) { clearTimeout(module.timer); module.timer = setTimeout(function () { $context.triggerHandler('scrollchange' + eventNamespace, [$context.scrollTop()]); }, settings.throttle); } else { requestAnimationFrame(function () { $context.triggerHandler('scrollchange' + eventNamespace, [$context.scrollTop()]); }); } }, // subscribes to scrollchange scrollchange: function (event, scrollPosition) { module.checkVisibility(scrollPosition); }, }, precache: function (images, callback) { if (!Array.isArray(images)) { images = [images]; } var imagesLength = images.length, loadedCounter = 0, cache = [], cacheImage = document.createElement('img'), handleLoad = function () { loadedCounter++; if (loadedCounter >= images.length) { if (isFunction(callback)) { callback(); } } } ; while (imagesLength--) { cacheImage = document.createElement('img'); cacheImage.addEventListener('load', handleLoad); cacheImage.addEventListener('error', handleLoad); cacheImage.src = images[imagesLength]; cache.push(cacheImage); } }, enableCallbacks: function () { module.debug('Allowing callbacks to occur'); disabled = false; }, disableCallbacks: function () { module.debug('Disabling all callbacks temporarily'); disabled = true; }, should: { trackChanges: function () { if (methodInvoked) { module.debug('One time query, no need to bind events'); return false; } module.debug('Callbacks being attached'); return true; }, }, setup: { cache: function () { module.cache = { occurred: {}, screen: {}, element: {}, }; }, image: function () { var src = $module.data(metadata.src) ; if (src) { module.verbose('Lazy loading image', src); settings.once = true; settings.observeChanges = false; // show when top visible settings.onOnScreen = function () { module.debug('Image on screen', element); module.precache(src, function () { module.set.image(src, function () { loadedCount++; if (loadedCount === moduleCount) { settings.onAllLoaded.call(this); } settings.onLoad.call(this); }); }); }; } }, fixed: function () { module.debug('Setting up fixed'); settings.once = false; settings.observeChanges = false; settings.initialCheck = true; settings.refreshOnLoad = true; if (!parameters.transition) { settings.transition = false; } module.create.placeholder(); module.debug('Added placeholder', $placeholder); settings.onTopPassed = function () { module.debug('Element passed, adding fixed position', $module); module.show.placeholder(); module.set.fixed(); if (settings.transition) { if ($.fn.transition !== undefined) { $module.transition(settings.transition, settings.duration); } } }; settings.onTopPassedReverse = function () { module.debug('Element returned to position, removing fixed', $module); module.hide.placeholder(); module.remove.fixed(); }; }, }, create: { placeholder: function () { module.verbose('Creating fixed position placeholder'); $placeholder = $module .clone(false) .css('display', 'none') .addClass(className.placeholder) .insertAfter($module) ; }, }, show: { placeholder: function () { module.verbose('Showing placeholder'); $placeholder .css('display', 'block') .css('visibility', 'hidden') ; }, }, hide: { placeholder: function () { module.verbose('Hiding placeholder'); $placeholder .css('display', 'none') .css('visibility', '') ; }, }, set: { fixed: function () { module.verbose('Setting element to fixed position'); $module .addClass(className.fixed) .css({ position: 'fixed', top: settings.offset + 'px', left: 'auto', zIndex: settings.zIndex, }) ; settings.onFixed.call(element); }, image: function (src, callback) { $module .attr('src', src) ; if (settings.transition) { if ($.fn.transition !== undefined) { if ($module.hasClass(className.visible)) { module.debug('Transition already occurred on this image, skipping animation'); return; } $module.transition(settings.transition, settings.duration, callback); } else { $module.fadeIn(settings.duration, callback); } } else { $module.show(); } }, }, is: { onScreen: function () { var calculations = module.get.elementCalculations() ; return calculations.onScreen; }, offScreen: function () { var calculations = module.get.elementCalculations() ; return calculations.offScreen; }, visible: function () { if (module.cache && module.cache.element) { return !(module.cache.element.width === 0 && module.cache.element.offset.top === 0); } return false; }, verticallyScrollableContext: function () { var overflowY = $context[0] !== window ? $context.css('overflow-y') : false ; return overflowY === 'auto' || overflowY === 'scroll'; }, horizontallyScrollableContext: function () { var overflowX = $context[0] !== window ? $context.css('overflow-x') : false ; return overflowX === 'auto' || overflowX === 'scroll'; }, }, refresh: function () { module.debug('Refreshing constants (width/height)'); if (settings.type === 'fixed') { module.resetFixed(); } module.reset(); module.save.position(); if (settings.checkOnRefresh) { module.checkVisibility(); } settings.onRefresh.call(element); }, resetFixed: function () { module.remove.fixed(); module.remove.occurred(); }, reset: function () { module.verbose('Resetting all cached values'); if ($.isPlainObject(module.cache)) { module.cache.screen = {}; module.cache.element = {}; } }, checkVisibility: function (scroll) { module.verbose('Checking visibility of element', module.cache.element); if (!disabled && module.is.visible()) { // save scroll position module.save.scroll(scroll); // update calculations derived from scroll module.save.calculations(); // percentage module.passed(); // reverse (must be first) module.passingReverse(); module.topVisibleReverse(); module.bottomVisibleReverse(); module.topPassedReverse(); module.bottomPassedReverse(); // one time module.onScreen(); module.offScreen(); module.passing(); module.topVisible(); module.bottomVisible(); module.topPassed(); module.bottomPassed(); // on update callback if (settings.onUpdate) { settings.onUpdate.call(element, module.get.elementCalculations()); } } }, passed: function (amount, newCallback) { var calculations = module.get.elementCalculations() ; // assign callback if (amount && newCallback) { settings.onPassed[amount] = newCallback; } else if (amount !== undefined) { return module.get.pixelsPassed(amount) > calculations.pixelsPassed; } else if (calculations.passing) { $.each(settings.onPassed, function (amount, callback) { if (calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) { module.execute(callback, amount); } else if (!settings.once) { module.remove.occurred(callback); } }); } }, onScreen: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onOnScreen, callbackName = 'onScreen' ; if (newCallback) { module.debug('Adding callback for onScreen', newCallback); settings.onOnScreen = newCallback; } if (calculations.onScreen) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback !== undefined) { return calculations.onOnScreen; } }, offScreen: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onOffScreen, callbackName = 'offScreen' ; if (newCallback) { module.debug('Adding callback for offScreen', newCallback); settings.onOffScreen = newCallback; } if (calculations.offScreen) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback !== undefined) { return calculations.onOffScreen; } }, passing: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onPassing, callbackName = 'passing' ; if (newCallback) { module.debug('Adding callback for passing', newCallback); settings.onPassing = newCallback; } if (calculations.passing) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback !== undefined) { return calculations.passing; } }, topVisible: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onTopVisible, callbackName = 'topVisible' ; if (newCallback) { module.debug('Adding callback for top visible', newCallback); settings.onTopVisible = newCallback; } if (calculations.topVisible) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return calculations.topVisible; } }, bottomVisible: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onBottomVisible, callbackName = 'bottomVisible' ; if (newCallback) { module.debug('Adding callback for bottom visible', newCallback); settings.onBottomVisible = newCallback; } if (calculations.bottomVisible) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return calculations.bottomVisible; } }, topPassed: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onTopPassed, callbackName = 'topPassed' ; if (newCallback) { module.debug('Adding callback for top passed', newCallback); settings.onTopPassed = newCallback; } if (calculations.topPassed) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return calculations.topPassed; } }, bottomPassed: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onBottomPassed, callbackName = 'bottomPassed' ; if (newCallback) { module.debug('Adding callback for bottom passed', newCallback); settings.onBottomPassed = newCallback; } if (calculations.bottomPassed) { module.execute(callback, callbackName); } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return calculations.bottomPassed; } }, passingReverse: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onPassingReverse, callbackName = 'passingReverse' ; if (newCallback) { module.debug('Adding callback for passing reverse', newCallback); settings.onPassingReverse = newCallback; } if (!calculations.passing) { if (module.get.occurred('passing')) { module.execute(callback, callbackName); } } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback !== undefined) { return !calculations.passing; } }, topVisibleReverse: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onTopVisibleReverse, callbackName = 'topVisibleReverse' ; if (newCallback) { module.debug('Adding callback for top visible reverse', newCallback); settings.onTopVisibleReverse = newCallback; } if (!calculations.topVisible) { if (module.get.occurred('topVisible')) { module.execute(callback, callbackName); } } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return !calculations.topVisible; } }, bottomVisibleReverse: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onBottomVisibleReverse, callbackName = 'bottomVisibleReverse' ; if (newCallback) { module.debug('Adding callback for bottom visible reverse', newCallback); settings.onBottomVisibleReverse = newCallback; } if (!calculations.bottomVisible) { if (module.get.occurred('bottomVisible')) { module.execute(callback, callbackName); } } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return !calculations.bottomVisible; } }, topPassedReverse: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onTopPassedReverse, callbackName = 'topPassedReverse' ; if (newCallback) { module.debug('Adding callback for top passed reverse', newCallback); settings.onTopPassedReverse = newCallback; } if (!calculations.topPassed) { if (module.get.occurred('topPassed')) { module.execute(callback, callbackName); } } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return !calculations.onTopPassed; } }, bottomPassedReverse: function (newCallback) { var calculations = module.get.elementCalculations(), callback = newCallback || settings.onBottomPassedReverse, callbackName = 'bottomPassedReverse' ; if (newCallback) { module.debug('Adding callback for bottom passed reverse', newCallback); settings.onBottomPassedReverse = newCallback; } if (!calculations.bottomPassed) { if (module.get.occurred('bottomPassed')) { module.execute(callback, callbackName); } } else if (!settings.once) { module.remove.occurred(callbackName); } if (newCallback === undefined) { return !calculations.bottomPassed; } }, execute: function (callback, callbackName) { var calculations = module.get.elementCalculations(), screen = module.get.screenCalculations() ; callback = callback || false; if (callback) { if (settings.continuous) { module.debug('Callback being called continuously', callbackName, calculations); callback.call(element, calculations, screen); } else if (!module.get.occurred(callbackName)) { module.debug('Conditions met', callbackName, calculations); callback.call(element, calculations, screen); } } module.save.occurred(callbackName); }, remove: { fixed: function () { module.debug('Removing fixed position'); $module .removeClass(className.fixed) .css({ position: '', top: '', left: '', zIndex: '', }) ; settings.onUnfixed.call(element); }, placeholder: function () { module.debug('Removing placeholder content'); if ($placeholder) { $placeholder.remove(); } }, occurred: function (callback) { if (callback) { var occurred = module.cache.occurred ; if (occurred[callback] !== undefined && occurred[callback] === true) { module.debug('Callback can now be called again', callback); module.cache.occurred[callback] = false; } } else { module.cache.occurred = {}; } }, }, save: { calculations: function () { module.verbose('Saving all calculations necessary to determine positioning'); module.save.direction(); module.save.screenCalculations(); module.save.elementCalculations(); }, occurred: function (callback) { if (callback) { if (module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) { module.verbose('Saving callback occurred', callback); module.cache.occurred[callback] = true; } } }, scroll: function (scrollPosition) { scrollPosition = scrollPosition + settings.offset || $context.scrollTop() + settings.offset; module.cache.scroll = scrollPosition; }, direction: function () { var scroll = module.get.scroll(), lastScroll = module.get.lastScroll(), direction ; if (scroll > lastScroll && lastScroll) { direction = 'down'; } else if (scroll < lastScroll && lastScroll) { direction = 'up'; } else { direction = 'static'; } module.cache.direction = direction; return module.cache.direction; }, elementPosition: function () { var element = module.cache.element, screen = module.get.screenSize() ; module.verbose('Saving element position'); // (quicker than $.extend) element.fits = element.height < screen.height; element.offset = $module.offset(); element.width = $module.outerWidth(); element.height = $module.outerHeight(); // compensate for scroll in context if (module.is.verticallyScrollableContext()) { element.offset.top += $context.scrollTop() - $context.offset().top; } if (module.is.horizontallyScrollableContext()) { element.offset.left += $context.scrollLeft() - $context.offset().left; } // store module.cache.element = element; return element; }, elementCalculations: function () { var screen = module.get.screenCalculations(), element = module.get.elementPosition() ; // offset if (settings.includeMargin) { element.margin = {}; element.margin.top = parseInt($module.css('margin-top'), 10); element.margin.bottom = parseInt($module.css('margin-bottom'), 10); element.top = element.offset.top - element.margin.top; element.bottom = element.offset.top + element.height + element.margin.bottom; } else { element.top = element.offset.top; element.bottom = element.offset.top + element.height; } // visibility element.topPassed = screen.top >= element.top; element.bottomPassed = screen.top >= element.bottom; element.topVisible = (screen.bottom >= element.top) && !element.topPassed; element.bottomVisible = (screen.bottom >= element.bottom) && !element.bottomPassed; element.pixelsPassed = 0; element.percentagePassed = 0; // meta calculations element.onScreen = (element.topVisible || element.passing) && !element.bottomPassed; element.passing = element.topPassed && !element.bottomPassed; element.offScreen = !element.onScreen; // passing calculations if (element.passing) { element.pixelsPassed = screen.top - element.top; element.percentagePassed = (screen.top - element.top) / element.height; } module.cache.element = element; module.verbose('Updated element calculations', element); return element; }, screenCalculations: function () { var scroll = module.get.scroll() ; module.save.direction(); module.cache.screen.top = scroll; module.cache.screen.bottom = scroll + module.cache.screen.height; return module.cache.screen; }, screenSize: function () { module.verbose('Saving window position'); module.cache.screen = { height: $context.height(), }; }, position: function () { module.save.screenSize(); module.save.elementPosition(); }, }, get: { pixelsPassed: function (amount) { var element = module.get.elementCalculations() ; if (amount.search('%') > -1) { return element.height * (parseInt(amount, 10) / 100); } return parseInt(amount, 10); }, occurred: function (callback) { return module.cache.occurred !== undefined ? module.cache.occurred[callback] || false : false; }, direction: function () { if (module.cache.direction === undefined) { module.save.direction(); } return module.cache.direction; }, elementPosition: function () { if (module.cache.element === undefined) { module.save.elementPosition(); } return module.cache.element; }, elementCalculations: function () { if (module.cache.element === undefined) { module.save.elementCalculations(); } return module.cache.element; }, screenCalculations: function () { if (module.cache.screen === undefined) { module.save.screenCalculations(); } return module.cache.screen; }, screenSize: function () { if (module.cache.screen === undefined) { module.save.screenSize(); } return module.cache.screen; }, scroll: function () { if (module.cache.scroll === undefined) { module.save.scroll(); } return module.cache.scroll; }, lastScroll: function () { if (module.cache.screen === undefined) { module.debug('First scroll event, no last scroll could be found'); return false; } return module.cache.screen.top; }, }, setting: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, settings, name); } else if (value !== undefined) { settings[name] = value; } else { return settings[name]; } }, internal: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, module, name); } else if (value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function () { if (!settings.silent && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function () { if (!settings.silent && settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function () { if (!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } }, performance: { log: function (message) { var currentTime, executionTime, previousTime ; if (settings.performance) { currentTime = Date.now(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ Name: message[0], Arguments: [].slice.call(message, 1) || '', Element: element, 'Execution Time': executionTime, }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(function () { module.performance.display(); }, 500); }, display: function () { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function (index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if (performance.length > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data) { console.log(data.Name + ': ' + data['Execution Time'] + 'ms'); }); } console.groupEnd(); } performance = []; }, }, invoke: function (query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if (typeof query === 'string' && object !== undefined) { query = query.split(/[ .]/); maxDepth = query.length - 1; $.each(query, function (depth, value) { var camelCaseValue = depth !== maxDepth ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query ; if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false; } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if (isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if (Array.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if (response !== undefined) { returnedValue = response; } return found; }, }; if (methodInvoked) { if (instance === undefined) { module.initialize(); } instance.save.scroll(); instance.save.calculations(); module.invoke(query); } else { if (instance !== undefined) { instance.invoke('destroy'); } module.initialize(); } }); return returnedValue !== undefined ? returnedValue : this; }; $.fn.visibility.settings = { name: 'Visibility', namespace: 'visibility', debug: false, verbose: false, performance: true, // whether to use mutation observers to follow changes observeChanges: true, // check position immediately on init initialCheck: true, // whether to refresh calculations after all page images load refreshOnLoad: true, // whether to refresh calculations after page resize event refreshOnResize: true, // should call callbacks on refresh event (resize, etc) checkOnRefresh: true, // callback should only occur one time once: true, // callback should fire continuously when evaluates to true continuous: false, // offset to use with scroll top offset: 0, // whether to include margin in elements position includeMargin: false, // scroll context for visibility checks context: window, // visibility check delay in ms (defaults to animationFrame) throttle: false, // special visibility type (image, fixed) type: false, // z-index to use with visibility 'fixed' zIndex: '10', // image only animation settings transition: 'fade in', duration: 1000, // array of callbacks for percentage onPassed: {}, // standard callbacks onOnScreen: false, onOffScreen: false, onPassing: false, onTopVisible: false, onBottomVisible: false, onTopPassed: false, onBottomPassed: false, // reverse callbacks onPassingReverse: false, onTopVisibleReverse: false, onBottomVisibleReverse: false, onTopPassedReverse: false, onBottomPassedReverse: false, // special callbacks for image onLoad: function () {}, onAllLoaded: function () {}, // special callbacks for fixed position onFixed: function () {}, onUnfixed: function () {}, // utility callbacks onUpdate: false, // disabled by default for performance onRefresh: function () {}, metadata: { src: 'src', }, className: { fixed: 'fixed', placeholder: 'constraint', visible: 'visible', }, error: { method: 'The method you called is not defined.', visible: 'Element is hidden, you must call refresh after element becomes visible', }, }; })(jQuery, window, document);