You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1311 lines
42 KiB
1311 lines
42 KiB
/*! |
|
* # Semantic UI 2.4.1 - Visibility |
|
* http://github.com/semantic-org/semantic-ui/ |
|
* |
|
* |
|
* Released under the MIT license |
|
* http://opensource.org/licenses/MIT |
|
* |
|
*/ |
|
|
|
;(function ($, window, document, undefined) { |
|
|
|
'use strict'; |
|
|
|
window = (typeof window != 'undefined' && window.Math == Math) |
|
? window |
|
: (typeof self != 'undefined' && self.Math == Math) |
|
? self |
|
: Function('return this')() |
|
; |
|
|
|
$.fn.visibility = function(parameters) { |
|
var |
|
$allModules = $(this), |
|
moduleSelector = $allModules.selector || '', |
|
|
|
time = new Date().getTime(), |
|
performance = [], |
|
|
|
query = arguments[0], |
|
methodInvoked = (typeof query == 'string'), |
|
queryArguments = [].slice.call(arguments, 1), |
|
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 = $(settings.context), |
|
|
|
$placeholder, |
|
|
|
selector = $module.selector || '', |
|
instance = $module.data(moduleNamespace), |
|
|
|
requestAnimationFrame = window.requestAnimationFrame |
|
|| window.mozRequestAnimationFrame |
|
|| window.webkitRequestAnimationFrame |
|
|| window.msRequestAnimationFrame |
|
|| function(callback) { setTimeout(callback, 0); }, |
|
|
|
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 (!(images instanceof Array)) { |
|
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.onload = handleLoad; |
|
cacheImage.onerror = 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.get(0) !== window) |
|
? $context.css('overflow-y') |
|
: false |
|
; |
|
return (overflowY == 'auto' || overflowY == 'scroll'); |
|
}, |
|
horizontallyScrollableContext: function() { |
|
var |
|
overflowX = ($context.get(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(), |
|
amountInPixels |
|
; |
|
// 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 = new Date().getTime(); |
|
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(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(moduleSelector) { |
|
title += ' \'' + moduleSelector + '\''; |
|
} |
|
if( (console.group !== undefined || console.table !== undefined) && 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 = element || context; |
|
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($.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 whe 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 : 'placeholder', |
|
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 );
|
|
|