text: /*!
   * # Semantic UI 2.2.8 - 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 ) {
                  $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;
            }
          },
  
          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();
              // 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.topVisible       = (screen.bottom >= element.top);
              element.topPassed        = (screen.top >= element.top);
              element.bottomVisible    = (screen.bottom >= element.bottom);
              element.bottomPassed     = (screen.top >= element.bottom);
              element.pixelsPassed     = 0;
              element.percentagePassed = 0;
  
              // meta calculations
              element.onScreen  = (element.topVisible && !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'
    },
  
    error : {
      method  : 'The method you called is not defined.',
      visible : 'Element is hidden, you must call refresh after element becomes visible'
    }
  
  };
  
  })( jQuery, window, document );
  