github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/semantic/dist/components/visibility.js (about)

     1  /*!
     2   * # Semantic UI x.x - Visibility
     3   * http://github.com/semantic-org/semantic-ui/
     4   *
     5   *
     6   * Copyright 2014 Contributors
     7   * Released under the MIT license
     8   * http://opensource.org/licenses/MIT
     9   *
    10   */
    11  
    12  ;(function ( $, window, document, undefined ) {
    13  
    14  "use strict";
    15  
    16  $.fn.visibility = function(parameters) {
    17    var
    18      $allModules    = $(this),
    19      moduleSelector = $allModules.selector || '',
    20  
    21      time           = new Date().getTime(),
    22      performance    = [],
    23  
    24      query          = arguments[0],
    25      methodInvoked  = (typeof query == 'string'),
    26      queryArguments = [].slice.call(arguments, 1),
    27      returnedValue
    28    ;
    29  
    30    $allModules
    31      .each(function() {
    32        var
    33          settings        = ( $.isPlainObject(parameters) )
    34            ? $.extend(true, {}, $.fn.visibility.settings, parameters)
    35            : $.extend({}, $.fn.visibility.settings),
    36  
    37          className       = settings.className,
    38          namespace       = settings.namespace,
    39          error           = settings.error,
    40  
    41          eventNamespace  = '.' + namespace,
    42          moduleNamespace = 'module-' + namespace,
    43  
    44          $window         = $(window),
    45          $module         = $(this),
    46          $context        = $(settings.context),
    47          $images         = $module.find('img'),
    48  
    49          selector        = $module.selector || '',
    50          instance        = $module.data(moduleNamespace),
    51  
    52          requestAnimationFrame = window.requestAnimationFrame
    53            || window.mozRequestAnimationFrame
    54            || window.webkitRequestAnimationFrame
    55            || window.msRequestAnimationFrame
    56            || function(callback) { setTimeout(callback, 0); },
    57  
    58          element         = this,
    59          observer,
    60          module
    61        ;
    62  
    63        module = {
    64  
    65          initialize: function() {
    66            module.debug('Initializing', settings);
    67  
    68            module.setup.cache();
    69            module.save.position();
    70  
    71            if( module.should.trackChanges() ) {
    72              module.bind.events();
    73              if(settings.type == 'image') {
    74                module.setup.image();
    75              }
    76              if(settings.type == 'fixed') {
    77                module.setup.fixed();
    78              }
    79            }
    80            if(settings.initialCheck) {
    81              module.checkVisibility();
    82            }
    83            if(settings.observeChanges) {
    84              module.observeChanges();
    85            }
    86            module.instantiate();
    87          },
    88  
    89          instantiate: function() {
    90            module.debug('Storing instance', module);
    91            $module
    92              .data(moduleNamespace, module)
    93            ;
    94            instance = module;
    95          },
    96  
    97          destroy: function() {
    98            module.verbose('Destroying previous module');
    99            $module
   100              .off(eventNamespace)
   101              .removeData(moduleNamespace)
   102            ;
   103            $window.off('resize' + eventNamespace, module.event.refresh);
   104            $context.off('scroll' + eventNamespace, module.event.scroll);
   105          },
   106  
   107          observeChanges: function() {
   108            var
   109              context = $context[0]
   110            ;
   111            if('MutationObserver' in window) {
   112              observer = new MutationObserver(function(mutations) {
   113                module.verbose('DOM tree modified, updating visibility calculations');
   114                module.refresh();
   115              });
   116              observer.observe(element, {
   117                childList : true,
   118                subtree   : true
   119              });
   120              module.debug('Setting up mutation observer', observer);
   121            }
   122          },
   123  
   124          bind: {
   125            events: function() {
   126              module.verbose('Binding visibility events to scroll and resize');
   127              $window
   128                .on('resize' + eventNamespace, module.event.refresh)
   129              ;
   130              $context
   131                .on('scroll' + eventNamespace, module.event.scroll)
   132              ;
   133              if($images.length > 0) {
   134                module.bind.imageLoad();
   135              }
   136            },
   137            imageLoad: function() {
   138              var
   139                imageCount    = $images.length,
   140                index         = imageCount,
   141                loadedCount   = 0,
   142                images        = [],
   143                cache         = [],
   144                cacheImage    = document.createElement('img'),
   145                handleLoad    = function() {
   146                  loadedCount++;
   147                  if(loadedCount >= imageCount) {
   148                    module.debug('Images finished loading inside element, refreshing position');
   149                    module.refresh();
   150                  }
   151                }
   152              ;
   153              $images
   154                .each(function() {
   155                  images.push( $(this).attr('src') );
   156                })
   157              ;
   158              while(index--) {
   159                cacheImage         = document.createElement('img');
   160                cacheImage.onload  = handleLoad;
   161                cacheImage.onerror = handleLoad;
   162                cacheImage.src     = images[index];
   163                cache.push(cacheImage);
   164              }
   165            }
   166          },
   167  
   168          event: {
   169            refresh: function() {
   170              requestAnimationFrame(module.refresh);
   171            },
   172            scroll: function() {
   173              module.verbose('Scroll position changed');
   174              if(settings.throttle) {
   175                clearTimeout(module.timer);
   176                module.timer = setTimeout(function() {
   177                  module.checkVisibility();
   178                }, settings.throttle);
   179              }
   180              else {
   181                requestAnimationFrame(function() {
   182                  module.checkVisibility();
   183                });
   184              }
   185            }
   186          },
   187  
   188          should: {
   189            trackChanges: function() {
   190              if(methodInvoked && queryArguments.length > 0) {
   191                module.debug('One time query, no need to bind events');
   192                return false;
   193              }
   194              module.debug('Callbacks being attached');
   195              return true;
   196            }
   197          },
   198  
   199          setup: {
   200            cache: function() {
   201              module.cache = {
   202                occurred : {},
   203                screen   : {},
   204                element  : {},
   205              };
   206            },
   207            image: function() {
   208              var
   209                src = $module.data('src')
   210              ;
   211              if(src) {
   212                module.verbose('Lazy loading image', src);
   213                settings.observeChanges = false;
   214                // show when top visible
   215                module.topVisible(function() {
   216                  module.debug('Image top visible', element);
   217                  module.precache(src, function() {
   218                    module.set.image(src);
   219                    settings.onTopVisible = false;
   220                  });
   221                });
   222              }
   223            },
   224            fixed: function() {
   225              module.verbose('Setting up fixed on element pass');
   226              settings.once = false;
   227              settings.onTopPassed = function() {
   228                $module
   229                  .addClass(className.fixed)
   230                  .css({
   231                    top: settings.offset + 'px'
   232                  })
   233                ;
   234                if(settings.transition) {
   235                  if($.fn.transition !== undefined) {
   236                    $module.transition(settings.transition, settings.duration);
   237                  }
   238                }
   239              };
   240              settings.onTopPassedReverse = function() {
   241                $module
   242                  .removeClass(className.fixed)
   243                  .css({
   244                    position: '',
   245                    top: ''
   246                  })
   247                ;
   248              };
   249            }
   250          },
   251  
   252          set: {
   253            image: function(src) {
   254              var
   255                offScreen = (module.cache.screen.bottom < module.cache.element.top)
   256              ;
   257              $module
   258                .attr('src', src)
   259              ;
   260              if(offScreen) {
   261                module.verbose('Image outside browser, no show animation');
   262                $module.show();
   263              }
   264              else {
   265                if(settings.transition) {
   266                  if( $.fn.transition !== undefined ) {
   267                    $module.transition(settings.transition, settings.duration);
   268                  }
   269                  else {
   270                    $module.fadeIn(settings.duration);
   271                  }
   272                }
   273                else {
   274                  $module.show();
   275                }
   276              }
   277            }
   278          },
   279  
   280          is: {
   281            visible: function() {
   282              if(module.cache && module.cache.element) {
   283                return (module.cache.element.width > 0);
   284              }
   285              return false;
   286            }
   287          },
   288  
   289          refresh: function() {
   290            module.debug('Refreshing constants (element width/height)');
   291            module.reset();
   292            module.save.position();
   293            module.checkVisibility();
   294            settings.onRefresh.call(element);
   295          },
   296  
   297          reset: function() {
   298            module.verbose('Reseting all cached values');
   299            if( $.isPlainObject(module.cache) ) {
   300              module.cache.screen = {};
   301              module.cache.element = {};
   302            }
   303          },
   304  
   305          checkVisibility: function() {
   306            module.verbose('Checking visibility of element', module.cache.element);
   307  
   308            if( module.is.visible() ) {
   309  
   310              // update calculations derived from scroll
   311              module.save.calculations();
   312  
   313              // percentage
   314              module.passed();
   315  
   316              // reverse (must be first)
   317              module.passingReverse();
   318              module.topVisibleReverse();
   319              module.bottomVisibleReverse();
   320              module.topPassedReverse();
   321              module.bottomPassedReverse();
   322  
   323              // one time
   324              module.passing();
   325              module.topVisible();
   326              module.bottomVisible();
   327              module.topPassed();
   328              module.bottomPassed();
   329  
   330              // on update callback
   331              if(settings.onUpdate) {
   332                settings.onUpdate.call(element, module.get.elementCalculations());
   333              }
   334            }
   335          },
   336  
   337          passed: function(amount, newCallback) {
   338            var
   339              calculations   = module.get.elementCalculations(),
   340              amountInPixels
   341            ;
   342            // assign callback
   343            if(amount !== undefined && newCallback !== undefined) {
   344              settings.onPassed[amount] = newCallback;
   345            }
   346            else if(amount !== undefined) {
   347              return (module.get.pixelsPassed(amount) > calculations.pixelsPassed);
   348            }
   349            else if(calculations.passing) {
   350              $.each(settings.onPassed, function(amount, callback) {
   351                if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
   352                  module.execute(callback, amount);
   353                }
   354                else if(!settings.once) {
   355                  module.remove.occurred(callback);
   356                }
   357              });
   358            }
   359          },
   360  
   361          passing: function(newCallback) {
   362            var
   363              calculations = module.get.elementCalculations(),
   364              callback     = newCallback || settings.onPassing,
   365              callbackName = 'passing'
   366            ;
   367            if(newCallback) {
   368              module.debug('Adding callback for passing', newCallback);
   369              settings.onPassing = newCallback;
   370            }
   371            if(calculations.passing) {
   372              module.execute(callback, callbackName);
   373            }
   374            else if(!settings.once) {
   375              module.remove.occurred(callbackName);
   376            }
   377            if(newCallback !== undefined) {
   378              return calculations.passing;
   379            }
   380          },
   381  
   382  
   383          topVisible: function(newCallback) {
   384            var
   385              calculations = module.get.elementCalculations(),
   386              callback     = newCallback || settings.onTopVisible,
   387              callbackName = 'topVisible'
   388            ;
   389            if(newCallback) {
   390              module.debug('Adding callback for top visible', newCallback);
   391              settings.onTopVisible = newCallback;
   392            }
   393            if(calculations.topVisible) {
   394              module.execute(callback, callbackName);
   395            }
   396            else if(!settings.once) {
   397              module.remove.occurred(callbackName);
   398            }
   399            if(newCallback === undefined) {
   400              return calculations.topVisible;
   401            }
   402          },
   403  
   404          bottomVisible: function(newCallback) {
   405            var
   406              calculations = module.get.elementCalculations(),
   407              callback     = newCallback || settings.onBottomVisible,
   408              callbackName = 'bottomVisible'
   409            ;
   410            if(newCallback) {
   411              module.debug('Adding callback for bottom visible', newCallback);
   412              settings.onBottomVisible = newCallback;
   413            }
   414            if(calculations.bottomVisible) {
   415              module.execute(callback, callbackName);
   416            }
   417            else if(!settings.once) {
   418              module.remove.occurred(callbackName);
   419            }
   420            if(newCallback === undefined) {
   421              return calculations.bottomVisible;
   422            }
   423          },
   424  
   425          topPassed: function(newCallback) {
   426            var
   427              calculations = module.get.elementCalculations(),
   428              callback     = newCallback || settings.onTopPassed,
   429              callbackName = 'topPassed'
   430            ;
   431            if(newCallback) {
   432              module.debug('Adding callback for top passed', newCallback);
   433              settings.onTopPassed = newCallback;
   434            }
   435            if(calculations.topPassed) {
   436              module.execute(callback, callbackName);
   437            }
   438            else if(!settings.once) {
   439              module.remove.occurred(callbackName);
   440            }
   441            if(newCallback === undefined) {
   442              return calculations.topPassed;
   443            }
   444          },
   445  
   446          bottomPassed: function(newCallback) {
   447            var
   448              calculations = module.get.elementCalculations(),
   449              callback     = newCallback || settings.onBottomPassed,
   450              callbackName = 'bottomPassed'
   451            ;
   452            if(newCallback) {
   453              module.debug('Adding callback for bottom passed', newCallback);
   454              settings.onBottomPassed = newCallback;
   455            }
   456            if(calculations.bottomPassed) {
   457              module.execute(callback, callbackName);
   458            }
   459            else if(!settings.once) {
   460              module.remove.occurred(callbackName);
   461            }
   462            if(newCallback === undefined) {
   463              return calculations.bottomPassed;
   464            }
   465          },
   466  
   467          passingReverse: function(newCallback) {
   468            var
   469              calculations = module.get.elementCalculations(),
   470              callback     = newCallback || settings.onPassingReverse,
   471              callbackName = 'passingReverse'
   472            ;
   473            if(newCallback) {
   474              module.debug('Adding callback for passing reverse', newCallback);
   475              settings.onPassingReverse = newCallback;
   476            }
   477            if(!calculations.passing) {
   478              if(module.get.occurred('passing')) {
   479                module.execute(callback, callbackName);
   480              }
   481            }
   482            else if(!settings.once) {
   483              module.remove.occurred(callbackName);
   484            }
   485            if(newCallback !== undefined) {
   486              return !calculations.passing;
   487            }
   488          },
   489  
   490  
   491          topVisibleReverse: function(newCallback) {
   492            var
   493              calculations = module.get.elementCalculations(),
   494              callback     = newCallback || settings.onTopVisibleReverse,
   495              callbackName = 'topVisibleReverse'
   496            ;
   497            if(newCallback) {
   498              module.debug('Adding callback for top visible reverse', newCallback);
   499              settings.onTopVisibleReverse = newCallback;
   500            }
   501            if(!calculations.topVisible) {
   502              if(module.get.occurred('topVisible')) {
   503                module.execute(callback, callbackName);
   504              }
   505            }
   506            else if(!settings.once) {
   507              module.remove.occurred(callbackName);
   508            }
   509            if(newCallback === undefined) {
   510              return !calculations.topVisible;
   511            }
   512          },
   513  
   514          bottomVisibleReverse: function(newCallback) {
   515            var
   516              calculations = module.get.elementCalculations(),
   517              callback     = newCallback || settings.onBottomVisibleReverse,
   518              callbackName = 'bottomVisibleReverse'
   519            ;
   520            if(newCallback) {
   521              module.debug('Adding callback for bottom visible reverse', newCallback);
   522              settings.onBottomVisibleReverse = newCallback;
   523            }
   524            if(!calculations.bottomVisible) {
   525              if(module.get.occurred('bottomVisible')) {
   526                module.execute(callback, callbackName);
   527              }
   528            }
   529            else if(!settings.once) {
   530              module.remove.occurred(callbackName);
   531            }
   532            if(newCallback === undefined) {
   533              return !calculations.bottomVisible;
   534            }
   535          },
   536  
   537          topPassedReverse: function(newCallback) {
   538            var
   539              calculations = module.get.elementCalculations(),
   540              callback     = newCallback || settings.onTopPassedReverse,
   541              callbackName = 'topPassedReverse'
   542            ;
   543            if(newCallback) {
   544              module.debug('Adding callback for top passed reverse', newCallback);
   545              settings.onTopPassedReverse = newCallback;
   546            }
   547            if(!calculations.topPassed) {
   548              if(module.get.occurred('topPassed')) {
   549                module.execute(callback, callbackName);
   550              }
   551            }
   552            else if(!settings.once) {
   553              module.remove.occurred(callbackName);
   554            }
   555            if(newCallback === undefined) {
   556              return !calculations.onTopPassed;
   557            }
   558          },
   559  
   560          bottomPassedReverse: function(newCallback) {
   561            var
   562              calculations = module.get.elementCalculations(),
   563              callback     = newCallback || settings.onBottomPassedReverse,
   564              callbackName = 'bottomPassedReverse'
   565            ;
   566            if(newCallback) {
   567              module.debug('Adding callback for bottom passed reverse', newCallback);
   568              settings.onBottomPassedReverse = newCallback;
   569            }
   570            if(!calculations.bottomPassed) {
   571              if(module.get.occurred('bottomPassed')) {
   572                module.execute(callback, callbackName);
   573              }
   574            }
   575            else if(!settings.once) {
   576              module.remove.occurred(callbackName);
   577            }
   578            if(newCallback === undefined) {
   579              return !calculations.bottomPassed;
   580            }
   581          },
   582  
   583          execute: function(callback, callbackName) {
   584            var
   585              calculations = module.get.elementCalculations(),
   586              screen       = module.get.screenCalculations()
   587            ;
   588            callback = callback || false;
   589            if(callback) {
   590              if(settings.continuous) {
   591                module.debug('Callback being called continuously', callbackName, calculations);
   592                callback.call(element, calculations, screen);
   593              }
   594              else if(!module.get.occurred(callbackName)) {
   595                module.debug('Conditions met', callbackName, calculations);
   596                callback.call(element, calculations, screen);
   597              }
   598            }
   599            module.save.occurred(callbackName);
   600          },
   601  
   602          remove: {
   603            occurred: function(callback) {
   604              if(callback) {
   605                if(module.cache.occurred[callback] !== undefined && module.cache.occurred[callback] === true) {
   606                  module.debug('Callback can now be called again', callback);
   607                  module.cache.occurred[callback] = false;
   608                }
   609              }
   610              else {
   611                module.cache.occurred = {};
   612              }
   613            }
   614          },
   615  
   616          save: {
   617            calculations: function() {
   618              module.verbose('Saving all calculations necessary to determine positioning');
   619              module.save.scroll();
   620              module.save.direction();
   621              module.save.screenCalculations();
   622              module.save.elementCalculations();
   623            },
   624            occurred: function(callback) {
   625              if(callback) {
   626                if(module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) {
   627                  module.verbose('Saving callback occurred', callback);
   628                  module.cache.occurred[callback] = true;
   629                }
   630              }
   631            },
   632            scroll: function() {
   633              module.cache.scroll = $context.scrollTop() + settings.offset;
   634            },
   635            direction: function() {
   636              var
   637                scroll     = module.get.scroll(),
   638                lastScroll = module.get.lastScroll(),
   639                direction
   640              ;
   641              if(scroll > lastScroll && lastScroll) {
   642                direction = 'down';
   643              }
   644              else if(scroll < lastScroll && lastScroll) {
   645                direction = 'up';
   646              }
   647              else {
   648                direction = 'static';
   649              }
   650              module.cache.direction = direction;
   651              return module.cache.direction;
   652            },
   653            elementPosition: function() {
   654              var
   655                element = module.cache.element,
   656                screen  = module.get.screenSize()
   657              ;
   658              module.verbose('Saving element position');
   659              // (quicker than $.extend)
   660              element.fits          = (element.height < screen.height);
   661              element.offset        = $module.offset();
   662              element.width         = $module.outerWidth();
   663              element.height        = $module.outerHeight();
   664              // store
   665              module.cache.element = element;
   666              return element;
   667            },
   668            elementCalculations: function() {
   669              var
   670                screen     = module.get.screenCalculations(),
   671                element    = module.get.elementPosition()
   672              ;
   673              // offset
   674              if(settings.includeMargin) {
   675                element.margin        = {};
   676                element.margin.top    = parseInt($module.css('margin-top'), 10);
   677                element.margin.bottom = parseInt($module.css('margin-bottom'), 10);
   678                element.top    = element.offset.top - element.margin.top;
   679                element.bottom = element.offset.top + element.height + element.margin.bottom;
   680              }
   681              else {
   682                element.top    = element.offset.top;
   683                element.bottom = element.offset.top + element.height;
   684              }
   685  
   686              // visibility
   687              element.topVisible       = (screen.bottom >= element.top);
   688              element.topPassed        = (screen.top >= element.top);
   689              element.bottomVisible    = (screen.bottom >= element.bottom);
   690              element.bottomPassed     = (screen.top >= element.bottom);
   691              element.pixelsPassed     = 0;
   692              element.percentagePassed = 0;
   693  
   694              // meta calculations
   695              element.visible = (element.topVisible || element.bottomVisible);
   696              element.passing = (element.topPassed && !element.bottomPassed);
   697              element.hidden  = (!element.topVisible && !element.bottomVisible);
   698  
   699              // passing calculations
   700              if(element.passing) {
   701                element.pixelsPassed     = (screen.top - element.top);
   702                element.percentagePassed = (screen.top - element.top) / element.height;
   703              }
   704              module.cache.element = element;
   705              module.verbose('Updated element calculations', element);
   706              return element;
   707            },
   708            screenCalculations: function() {
   709              var
   710                scroll = module.get.scroll()
   711              ;
   712              module.save.direction();
   713              module.cache.screen.top    = scroll;
   714              module.cache.screen.bottom = scroll + module.cache.screen.height;
   715              return module.cache.screen;
   716            },
   717            screenSize: function() {
   718              module.verbose('Saving window position');
   719              module.cache.screen = {
   720                height: $context.height()
   721              };
   722            },
   723            position: function() {
   724              module.save.screenSize();
   725              module.save.elementPosition();
   726            }
   727          },
   728  
   729          get: {
   730            pixelsPassed: function(amount) {
   731              var
   732                element = module.get.elementCalculations()
   733              ;
   734              if(amount.search('%') > -1) {
   735                return ( element.height * (parseInt(amount, 10) / 100) );
   736              }
   737              return parseInt(amount, 10);
   738            },
   739            occurred: function(callback) {
   740              return (module.cache.occurred !== undefined)
   741                ? module.cache.occurred[callback] || false
   742                : false
   743              ;
   744            },
   745            direction: function() {
   746              if(module.cache.direction === undefined) {
   747                module.save.direction();
   748              }
   749              return module.cache.direction;
   750            },
   751            elementPosition: function() {
   752              if(module.cache.element === undefined) {
   753                module.save.elementPosition();
   754              }
   755              return module.cache.element;
   756            },
   757            elementCalculations: function() {
   758              if(module.cache.element === undefined) {
   759                module.save.elementCalculations();
   760              }
   761              return module.cache.element;
   762            },
   763            screenCalculations: function() {
   764              if(module.cache.screen === undefined) {
   765                module.save.screenCalculations();
   766              }
   767              return module.cache.screen;
   768            },
   769            screenSize: function() {
   770              if(module.cache.screen === undefined) {
   771                module.save.screenSize();
   772              }
   773              return module.cache.screen;
   774            },
   775            scroll: function() {
   776              if(module.cache.scroll === undefined) {
   777                module.save.scroll();
   778              }
   779              return module.cache.scroll;
   780            },
   781            lastScroll: function() {
   782              if(module.cache.screen === undefined) {
   783                module.debug('First scroll event, no last scroll could be found');
   784                return false;
   785              }
   786              return module.cache.screen.top;
   787            }
   788          },
   789  
   790          setting: function(name, value) {
   791            if( $.isPlainObject(name) ) {
   792              $.extend(true, settings, name);
   793            }
   794            else if(value !== undefined) {
   795              settings[name] = value;
   796            }
   797            else {
   798              return settings[name];
   799            }
   800          },
   801          internal: function(name, value) {
   802            if( $.isPlainObject(name) ) {
   803              $.extend(true, module, name);
   804            }
   805            else if(value !== undefined) {
   806              module[name] = value;
   807            }
   808            else {
   809              return module[name];
   810            }
   811          },
   812          debug: function() {
   813            if(settings.debug) {
   814              if(settings.performance) {
   815                module.performance.log(arguments);
   816              }
   817              else {
   818                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
   819                module.debug.apply(console, arguments);
   820              }
   821            }
   822          },
   823          verbose: function() {
   824            if(settings.verbose && settings.debug) {
   825              if(settings.performance) {
   826                module.performance.log(arguments);
   827              }
   828              else {
   829                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
   830                module.verbose.apply(console, arguments);
   831              }
   832            }
   833          },
   834          error: function() {
   835            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
   836            module.error.apply(console, arguments);
   837          },
   838          performance: {
   839            log: function(message) {
   840              var
   841                currentTime,
   842                executionTime,
   843                previousTime
   844              ;
   845              if(settings.performance) {
   846                currentTime   = new Date().getTime();
   847                previousTime  = time || currentTime;
   848                executionTime = currentTime - previousTime;
   849                time          = currentTime;
   850                performance.push({
   851                  'Name'           : message[0],
   852                  'Arguments'      : [].slice.call(message, 1) || '',
   853                  'Element'        : element,
   854                  'Execution Time' : executionTime
   855                });
   856              }
   857              clearTimeout(module.performance.timer);
   858              module.performance.timer = setTimeout(module.performance.display, 100);
   859            },
   860            display: function() {
   861              var
   862                title = settings.name + ':',
   863                totalTime = 0
   864              ;
   865              time = false;
   866              clearTimeout(module.performance.timer);
   867              $.each(performance, function(index, data) {
   868                totalTime += data['Execution Time'];
   869              });
   870              title += ' ' + totalTime + 'ms';
   871              if(moduleSelector) {
   872                title += ' \'' + moduleSelector + '\'';
   873              }
   874              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
   875                console.groupCollapsed(title);
   876                if(console.table) {
   877                  console.table(performance);
   878                }
   879                else {
   880                  $.each(performance, function(index, data) {
   881                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
   882                  });
   883                }
   884                console.groupEnd();
   885              }
   886              performance = [];
   887            }
   888          },
   889          invoke: function(query, passedArguments, context) {
   890            var
   891              object = instance,
   892              maxDepth,
   893              found,
   894              response
   895            ;
   896            passedArguments = passedArguments || queryArguments;
   897            context         = element         || context;
   898            if(typeof query == 'string' && object !== undefined) {
   899              query    = query.split(/[\. ]/);
   900              maxDepth = query.length - 1;
   901              $.each(query, function(depth, value) {
   902                var camelCaseValue = (depth != maxDepth)
   903                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
   904                  : query
   905                ;
   906                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
   907                  object = object[camelCaseValue];
   908                }
   909                else if( object[camelCaseValue] !== undefined ) {
   910                  found = object[camelCaseValue];
   911                  return false;
   912                }
   913                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
   914                  object = object[value];
   915                }
   916                else if( object[value] !== undefined ) {
   917                  found = object[value];
   918                  return false;
   919                }
   920                else {
   921                  module.error(error.method, query);
   922                  return false;
   923                }
   924              });
   925            }
   926            if ( $.isFunction( found ) ) {
   927              response = found.apply(context, passedArguments);
   928            }
   929            else if(found !== undefined) {
   930              response = found;
   931            }
   932            if($.isArray(returnedValue)) {
   933              returnedValue.push(response);
   934            }
   935            else if(returnedValue !== undefined) {
   936              returnedValue = [returnedValue, response];
   937            }
   938            else if(response !== undefined) {
   939              returnedValue = response;
   940            }
   941            return found;
   942          }
   943        };
   944  
   945        if(methodInvoked) {
   946          if(instance === undefined) {
   947            module.initialize();
   948          }
   949          module.invoke(query);
   950        }
   951        else {
   952          if(instance !== undefined) {
   953            instance.invoke('destroy');
   954          }
   955          module.initialize();
   956        }
   957      })
   958    ;
   959  
   960    return (returnedValue !== undefined)
   961      ? returnedValue
   962      : this
   963    ;
   964  };
   965  
   966  $.fn.visibility.settings = {
   967  
   968    name                   : 'Visibility',
   969    namespace              : 'visibility',
   970  
   971    debug                  : false,
   972    verbose                : false,
   973    performance            : true,
   974  
   975    // whether to use mutation observers to follow changes
   976    observeChanges         : true,
   977  
   978    // callback should only occur one time
   979    once                   : true,
   980  
   981    // callback should fire continuously whe evaluates to true
   982    continuous             : false,
   983  
   984    // offset to use with scroll top
   985    offset                 : 0,
   986  
   987    // whether to include margin in elements position
   988    includeMargin          : false,
   989  
   990    // scroll context for visibility checks
   991    context                : window,
   992  
   993    // check position immediately on init
   994    initialCheck           : true,
   995  
   996    // visibility check delay in ms (defaults to animationFrame)
   997    throttle               : false,
   998  
   999    // special visibility type (image, fixed)
  1000    type                   : false,
  1001  
  1002    // image only animation settings
  1003    transition             : false,
  1004    duration               : 1000,
  1005  
  1006    // array of callbacks for percentage
  1007    onPassed               : {},
  1008  
  1009    // standard callbacks
  1010    onPassing              : false,
  1011    onTopVisible           : false,
  1012    onBottomVisible        : false,
  1013    onTopPassed            : false,
  1014    onBottomPassed         : false,
  1015  
  1016    // reverse callbacks
  1017    onPassingReverse       : false,
  1018    onTopVisibleReverse    : false,
  1019    onBottomVisibleReverse : false,
  1020    onTopPassedReverse     : false,
  1021    onBottomPassedReverse  : false,
  1022  
  1023    // utility callbacks
  1024    onUpdate               : false, // disabled by default for performance
  1025    onRefresh              : function(){},
  1026  
  1027    className: {
  1028      fixed: 'fixed'
  1029    },
  1030  
  1031    error : {
  1032      method : 'The method you called is not defined.'
  1033    }
  1034  
  1035  };
  1036  
  1037  })( jQuery, window , document );