Code Duplication    Length = 1301-1301 lines in 2 locations

public/lib/semantic/semantic.js 1 location

@@ 21583-22883 (lines=1301) @@
21580
 *
21581
 */
21582
21583
;(function ($, window, document, undefined) {
21584
21585
"use strict";
21586
21587
window = (typeof window != 'undefined' && window.Math == Math)
21588
  ? window
21589
  : (typeof self != 'undefined' && self.Math == Math)
21590
    ? self
21591
    : Function('return this')()
21592
;
21593
21594
$.fn.visibility = function(parameters) {
21595
  var
21596
    $allModules    = $(this),
21597
    moduleSelector = $allModules.selector || '',
21598
21599
    time           = new Date().getTime(),
21600
    performance    = [],
21601
21602
    query          = arguments[0],
21603
    methodInvoked  = (typeof query == 'string'),
21604
    queryArguments = [].slice.call(arguments, 1),
21605
    returnedValue,
21606
21607
    moduleCount    = $allModules.length,
21608
    loadedCount    = 0
21609
  ;
21610
21611
  $allModules
21612
    .each(function() {
21613
      var
21614
        settings        = ( $.isPlainObject(parameters) )
21615
          ? $.extend(true, {}, $.fn.visibility.settings, parameters)
21616
          : $.extend({}, $.fn.visibility.settings),
21617
21618
        className       = settings.className,
21619
        namespace       = settings.namespace,
21620
        error           = settings.error,
21621
        metadata        = settings.metadata,
21622
21623
        eventNamespace  = '.' + namespace,
21624
        moduleNamespace = 'module-' + namespace,
21625
21626
        $window         = $(window),
21627
21628
        $module         = $(this),
21629
        $context        = $(settings.context),
21630
21631
        $placeholder,
21632
21633
        selector        = $module.selector || '',
21634
        instance        = $module.data(moduleNamespace),
21635
21636
        requestAnimationFrame = window.requestAnimationFrame
21637
          || window.mozRequestAnimationFrame
21638
          || window.webkitRequestAnimationFrame
21639
          || window.msRequestAnimationFrame
21640
          || function(callback) { setTimeout(callback, 0); },
21641
21642
        element         = this,
21643
        disabled        = false,
21644
21645
        contextObserver,
21646
        observer,
21647
        module
21648
      ;
21649
21650
      module = {
21651
21652
        initialize: function() {
21653
          module.debug('Initializing', settings);
21654
21655
          module.setup.cache();
21656
21657
          if( module.should.trackChanges() ) {
21658
21659
            if(settings.type == 'image') {
21660
              module.setup.image();
21661
            }
21662
            if(settings.type == 'fixed') {
21663
              module.setup.fixed();
21664
            }
21665
21666
            if(settings.observeChanges) {
21667
              module.observeChanges();
21668
            }
21669
            module.bind.events();
21670
          }
21671
21672
          module.save.position();
21673
          if( !module.is.visible() ) {
21674
            module.error(error.visible, $module);
21675
          }
21676
21677
          if(settings.initialCheck) {
21678
            module.checkVisibility();
21679
          }
21680
          module.instantiate();
21681
        },
21682
21683
        instantiate: function() {
21684
          module.debug('Storing instance', module);
21685
          $module
21686
            .data(moduleNamespace, module)
21687
          ;
21688
          instance = module;
21689
        },
21690
21691
        destroy: function() {
21692
          module.verbose('Destroying previous module');
21693
          if(observer) {
21694
            observer.disconnect();
21695
          }
21696
          if(contextObserver) {
21697
            contextObserver.disconnect();
21698
          }
21699
          $window
21700
            .off('load'   + eventNamespace, module.event.load)
21701
            .off('resize' + eventNamespace, module.event.resize)
21702
          ;
21703
          $context
21704
            .off('scroll'       + eventNamespace, module.event.scroll)
21705
            .off('scrollchange' + eventNamespace, module.event.scrollchange)
21706
          ;
21707
          if(settings.type == 'fixed') {
21708
            module.resetFixed();
21709
            module.remove.placeholder();
21710
          }
21711
          $module
21712
            .off(eventNamespace)
21713
            .removeData(moduleNamespace)
21714
          ;
21715
        },
21716
21717
        observeChanges: function() {
21718
          if('MutationObserver' in window) {
21719
            contextObserver = new MutationObserver(module.event.contextChanged);
21720
            observer        = new MutationObserver(module.event.changed);
21721
            contextObserver.observe(document, {
21722
              childList : true,
21723
              subtree   : true
21724
            });
21725
            observer.observe(element, {
21726
              childList : true,
21727
              subtree   : true
21728
            });
21729
            module.debug('Setting up mutation observer', observer);
21730
          }
21731
        },
21732
21733
        bind: {
21734
          events: function() {
21735
            module.verbose('Binding visibility events to scroll and resize');
21736
            if(settings.refreshOnLoad) {
21737
              $window
21738
                .on('load'   + eventNamespace, module.event.load)
21739
              ;
21740
            }
21741
            $window
21742
              .on('resize' + eventNamespace, module.event.resize)
21743
            ;
21744
            // pub/sub pattern
21745
            $context
21746
              .off('scroll'      + eventNamespace)
21747
              .on('scroll'       + eventNamespace, module.event.scroll)
21748
              .on('scrollchange' + eventNamespace, module.event.scrollchange)
21749
            ;
21750
          }
21751
        },
21752
21753
        event: {
21754
          changed: function(mutations) {
21755
            module.verbose('DOM tree modified, updating visibility calculations');
21756
            module.timer = setTimeout(function() {
21757
              module.verbose('DOM tree modified, updating sticky menu');
21758
              module.refresh();
21759
            }, 100);
21760
          },
21761
          contextChanged: function(mutations) {
21762
            [].forEach.call(mutations, function(mutation) {
21763
              if(mutation.removedNodes) {
21764
                [].forEach.call(mutation.removedNodes, function(node) {
21765
                  if(node == element || $(node).find(element).length > 0) {
21766
                    module.debug('Element removed from DOM, tearing down events');
21767
                    module.destroy();
21768
                  }
21769
                });
21770
              }
21771
            });
21772
          },
21773
          resize: function() {
21774
            module.debug('Window resized');
21775
            if(settings.refreshOnResize) {
21776
              requestAnimationFrame(module.refresh);
21777
            }
21778
          },
21779
          load: function() {
21780
            module.debug('Page finished loading');
21781
            requestAnimationFrame(module.refresh);
21782
          },
21783
          // publishes scrollchange event on one scroll
21784
          scroll: function() {
21785
            if(settings.throttle) {
21786
              clearTimeout(module.timer);
21787
              module.timer = setTimeout(function() {
21788
                $context.triggerHandler('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
21789
              }, settings.throttle);
21790
            }
21791
            else {
21792
              requestAnimationFrame(function() {
21793
                $context.triggerHandler('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
21794
              });
21795
            }
21796
          },
21797
          // subscribes to scrollchange
21798
          scrollchange: function(event, scrollPosition) {
21799
            module.checkVisibility(scrollPosition);
21800
          },
21801
        },
21802
21803
        precache: function(images, callback) {
21804
          if (!(images instanceof Array)) {
21805
            images = [images];
21806
          }
21807
          var
21808
            imagesLength  = images.length,
21809
            loadedCounter = 0,
21810
            cache         = [],
21811
            cacheImage    = document.createElement('img'),
21812
            handleLoad    = function() {
21813
              loadedCounter++;
21814
              if (loadedCounter >= images.length) {
21815
                if ($.isFunction(callback)) {
21816
                  callback();
21817
                }
21818
              }
21819
            }
21820
          ;
21821
          while (imagesLength--) {
21822
            cacheImage         = document.createElement('img');
21823
            cacheImage.onload  = handleLoad;
21824
            cacheImage.onerror = handleLoad;
21825
            cacheImage.src     = images[imagesLength];
21826
            cache.push(cacheImage);
21827
          }
21828
        },
21829
21830
        enableCallbacks: function() {
21831
          module.debug('Allowing callbacks to occur');
21832
          disabled = false;
21833
        },
21834
21835
        disableCallbacks: function() {
21836
          module.debug('Disabling all callbacks temporarily');
21837
          disabled = true;
21838
        },
21839
21840
        should: {
21841
          trackChanges: function() {
21842
            if(methodInvoked) {
21843
              module.debug('One time query, no need to bind events');
21844
              return false;
21845
            }
21846
            module.debug('Callbacks being attached');
21847
            return true;
21848
          }
21849
        },
21850
21851
        setup: {
21852
          cache: function() {
21853
            module.cache = {
21854
              occurred : {},
21855
              screen   : {},
21856
              element  : {},
21857
            };
21858
          },
21859
          image: function() {
21860
            var
21861
              src = $module.data(metadata.src)
21862
            ;
21863
            if(src) {
21864
              module.verbose('Lazy loading image', src);
21865
              settings.once           = true;
21866
              settings.observeChanges = false;
21867
21868
              // show when top visible
21869
              settings.onOnScreen = function() {
21870
                module.debug('Image on screen', element);
21871
                module.precache(src, function() {
21872
                  module.set.image(src, function() {
21873
                    loadedCount++;
21874
                    if(loadedCount == moduleCount) {
21875
                      settings.onAllLoaded.call(this);
21876
                    }
21877
                    settings.onLoad.call(this);
21878
                  });
21879
                });
21880
              };
21881
            }
21882
          },
21883
          fixed: function() {
21884
            module.debug('Setting up fixed');
21885
            settings.once           = false;
21886
            settings.observeChanges = false;
21887
            settings.initialCheck   = true;
21888
            settings.refreshOnLoad  = true;
21889
            if(!parameters.transition) {
21890
              settings.transition = false;
21891
            }
21892
            module.create.placeholder();
21893
            module.debug('Added placeholder', $placeholder);
21894
            settings.onTopPassed = function() {
21895
              module.debug('Element passed, adding fixed position', $module);
21896
              module.show.placeholder();
21897
              module.set.fixed();
21898
              if(settings.transition) {
21899
                if($.fn.transition !== undefined) {
21900
                  $module.transition(settings.transition, settings.duration);
21901
                }
21902
              }
21903
            };
21904
            settings.onTopPassedReverse = function() {
21905
              module.debug('Element returned to position, removing fixed', $module);
21906
              module.hide.placeholder();
21907
              module.remove.fixed();
21908
            };
21909
          }
21910
        },
21911
21912
        create: {
21913
          placeholder: function() {
21914
            module.verbose('Creating fixed position placeholder');
21915
            $placeholder = $module
21916
              .clone(false)
21917
              .css('display', 'none')
21918
              .addClass(className.placeholder)
21919
              .insertAfter($module)
21920
            ;
21921
          }
21922
        },
21923
21924
        show: {
21925
          placeholder: function() {
21926
            module.verbose('Showing placeholder');
21927
            $placeholder
21928
              .css('display', 'block')
21929
              .css('visibility', 'hidden')
21930
            ;
21931
          }
21932
        },
21933
        hide: {
21934
          placeholder: function() {
21935
            module.verbose('Hiding placeholder');
21936
            $placeholder
21937
              .css('display', 'none')
21938
              .css('visibility', '')
21939
            ;
21940
          }
21941
        },
21942
21943
        set: {
21944
          fixed: function() {
21945
            module.verbose('Setting element to fixed position');
21946
            $module
21947
              .addClass(className.fixed)
21948
              .css({
21949
                position : 'fixed',
21950
                top      : settings.offset + 'px',
21951
                left     : 'auto',
21952
                zIndex   : settings.zIndex
21953
              })
21954
            ;
21955
            settings.onFixed.call(element);
21956
          },
21957
          image: function(src, callback) {
21958
            $module
21959
              .attr('src', src)
21960
            ;
21961
            if(settings.transition) {
21962
              if( $.fn.transition !== undefined) {
21963
                if($module.hasClass(className.visible)) {
21964
                  module.debug('Transition already occurred on this image, skipping animation');
21965
                  return;
21966
                }
21967
                $module.transition(settings.transition, settings.duration, callback);
21968
              }
21969
              else {
21970
                $module.fadeIn(settings.duration, callback);
21971
              }
21972
            }
21973
            else {
21974
              $module.show();
21975
            }
21976
          }
21977
        },
21978
21979
        is: {
21980
          onScreen: function() {
21981
            var
21982
              calculations   = module.get.elementCalculations()
21983
            ;
21984
            return calculations.onScreen;
21985
          },
21986
          offScreen: function() {
21987
            var
21988
              calculations   = module.get.elementCalculations()
21989
            ;
21990
            return calculations.offScreen;
21991
          },
21992
          visible: function() {
21993
            if(module.cache && module.cache.element) {
21994
              return !(module.cache.element.width === 0 && module.cache.element.offset.top === 0);
21995
            }
21996
            return false;
21997
          },
21998
          verticallyScrollableContext: function() {
21999
            var
22000
              overflowY = ($context.get(0) !== window)
22001
                ? $context.css('overflow-y')
22002
                : false
22003
            ;
22004
            return (overflowY == 'auto' || overflowY == 'scroll');
22005
          },
22006
          horizontallyScrollableContext: function() {
22007
            var
22008
              overflowX = ($context.get(0) !== window)
22009
                ? $context.css('overflow-x')
22010
                : false
22011
            ;
22012
            return (overflowX == 'auto' || overflowX == 'scroll');
22013
          }
22014
        },
22015
22016
        refresh: function() {
22017
          module.debug('Refreshing constants (width/height)');
22018
          if(settings.type == 'fixed') {
22019
            module.resetFixed();
22020
          }
22021
          module.reset();
22022
          module.save.position();
22023
          if(settings.checkOnRefresh) {
22024
            module.checkVisibility();
22025
          }
22026
          settings.onRefresh.call(element);
22027
        },
22028
22029
        resetFixed: function () {
22030
          module.remove.fixed();
22031
          module.remove.occurred();
22032
        },
22033
22034
        reset: function() {
22035
          module.verbose('Resetting all cached values');
22036
          if( $.isPlainObject(module.cache) ) {
22037
            module.cache.screen = {};
22038
            module.cache.element = {};
22039
          }
22040
        },
22041
22042
        checkVisibility: function(scroll) {
22043
          module.verbose('Checking visibility of element', module.cache.element);
22044
22045
          if( !disabled && module.is.visible() ) {
22046
22047
            // save scroll position
22048
            module.save.scroll(scroll);
22049
22050
            // update calculations derived from scroll
22051
            module.save.calculations();
22052
22053
            // percentage
22054
            module.passed();
22055
22056
            // reverse (must be first)
22057
            module.passingReverse();
22058
            module.topVisibleReverse();
22059
            module.bottomVisibleReverse();
22060
            module.topPassedReverse();
22061
            module.bottomPassedReverse();
22062
22063
            // one time
22064
            module.onScreen();
22065
            module.offScreen();
22066
            module.passing();
22067
            module.topVisible();
22068
            module.bottomVisible();
22069
            module.topPassed();
22070
            module.bottomPassed();
22071
22072
            // on update callback
22073
            if(settings.onUpdate) {
22074
              settings.onUpdate.call(element, module.get.elementCalculations());
22075
            }
22076
          }
22077
        },
22078
22079
        passed: function(amount, newCallback) {
22080
          var
22081
            calculations   = module.get.elementCalculations(),
22082
            amountInPixels
22083
          ;
22084
          // assign callback
22085
          if(amount && newCallback) {
22086
            settings.onPassed[amount] = newCallback;
22087
          }
22088
          else if(amount !== undefined) {
22089
            return (module.get.pixelsPassed(amount) > calculations.pixelsPassed);
22090
          }
22091
          else if(calculations.passing) {
22092
            $.each(settings.onPassed, function(amount, callback) {
22093
              if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
22094
                module.execute(callback, amount);
22095
              }
22096
              else if(!settings.once) {
22097
                module.remove.occurred(callback);
22098
              }
22099
            });
22100
          }
22101
        },
22102
22103
        onScreen: function(newCallback) {
22104
          var
22105
            calculations = module.get.elementCalculations(),
22106
            callback     = newCallback || settings.onOnScreen,
22107
            callbackName = 'onScreen'
22108
          ;
22109
          if(newCallback) {
22110
            module.debug('Adding callback for onScreen', newCallback);
22111
            settings.onOnScreen = newCallback;
22112
          }
22113
          if(calculations.onScreen) {
22114
            module.execute(callback, callbackName);
22115
          }
22116
          else if(!settings.once) {
22117
            module.remove.occurred(callbackName);
22118
          }
22119
          if(newCallback !== undefined) {
22120
            return calculations.onOnScreen;
22121
          }
22122
        },
22123
22124
        offScreen: function(newCallback) {
22125
          var
22126
            calculations = module.get.elementCalculations(),
22127
            callback     = newCallback || settings.onOffScreen,
22128
            callbackName = 'offScreen'
22129
          ;
22130
          if(newCallback) {
22131
            module.debug('Adding callback for offScreen', newCallback);
22132
            settings.onOffScreen = newCallback;
22133
          }
22134
          if(calculations.offScreen) {
22135
            module.execute(callback, callbackName);
22136
          }
22137
          else if(!settings.once) {
22138
            module.remove.occurred(callbackName);
22139
          }
22140
          if(newCallback !== undefined) {
22141
            return calculations.onOffScreen;
22142
          }
22143
        },
22144
22145
        passing: function(newCallback) {
22146
          var
22147
            calculations = module.get.elementCalculations(),
22148
            callback     = newCallback || settings.onPassing,
22149
            callbackName = 'passing'
22150
          ;
22151
          if(newCallback) {
22152
            module.debug('Adding callback for passing', newCallback);
22153
            settings.onPassing = newCallback;
22154
          }
22155
          if(calculations.passing) {
22156
            module.execute(callback, callbackName);
22157
          }
22158
          else if(!settings.once) {
22159
            module.remove.occurred(callbackName);
22160
          }
22161
          if(newCallback !== undefined) {
22162
            return calculations.passing;
22163
          }
22164
        },
22165
22166
22167
        topVisible: function(newCallback) {
22168
          var
22169
            calculations = module.get.elementCalculations(),
22170
            callback     = newCallback || settings.onTopVisible,
22171
            callbackName = 'topVisible'
22172
          ;
22173
          if(newCallback) {
22174
            module.debug('Adding callback for top visible', newCallback);
22175
            settings.onTopVisible = newCallback;
22176
          }
22177
          if(calculations.topVisible) {
22178
            module.execute(callback, callbackName);
22179
          }
22180
          else if(!settings.once) {
22181
            module.remove.occurred(callbackName);
22182
          }
22183
          if(newCallback === undefined) {
22184
            return calculations.topVisible;
22185
          }
22186
        },
22187
22188
        bottomVisible: function(newCallback) {
22189
          var
22190
            calculations = module.get.elementCalculations(),
22191
            callback     = newCallback || settings.onBottomVisible,
22192
            callbackName = 'bottomVisible'
22193
          ;
22194
          if(newCallback) {
22195
            module.debug('Adding callback for bottom visible', newCallback);
22196
            settings.onBottomVisible = newCallback;
22197
          }
22198
          if(calculations.bottomVisible) {
22199
            module.execute(callback, callbackName);
22200
          }
22201
          else if(!settings.once) {
22202
            module.remove.occurred(callbackName);
22203
          }
22204
          if(newCallback === undefined) {
22205
            return calculations.bottomVisible;
22206
          }
22207
        },
22208
22209
        topPassed: function(newCallback) {
22210
          var
22211
            calculations = module.get.elementCalculations(),
22212
            callback     = newCallback || settings.onTopPassed,
22213
            callbackName = 'topPassed'
22214
          ;
22215
          if(newCallback) {
22216
            module.debug('Adding callback for top passed', newCallback);
22217
            settings.onTopPassed = newCallback;
22218
          }
22219
          if(calculations.topPassed) {
22220
            module.execute(callback, callbackName);
22221
          }
22222
          else if(!settings.once) {
22223
            module.remove.occurred(callbackName);
22224
          }
22225
          if(newCallback === undefined) {
22226
            return calculations.topPassed;
22227
          }
22228
        },
22229
22230
        bottomPassed: function(newCallback) {
22231
          var
22232
            calculations = module.get.elementCalculations(),
22233
            callback     = newCallback || settings.onBottomPassed,
22234
            callbackName = 'bottomPassed'
22235
          ;
22236
          if(newCallback) {
22237
            module.debug('Adding callback for bottom passed', newCallback);
22238
            settings.onBottomPassed = newCallback;
22239
          }
22240
          if(calculations.bottomPassed) {
22241
            module.execute(callback, callbackName);
22242
          }
22243
          else if(!settings.once) {
22244
            module.remove.occurred(callbackName);
22245
          }
22246
          if(newCallback === undefined) {
22247
            return calculations.bottomPassed;
22248
          }
22249
        },
22250
22251
        passingReverse: function(newCallback) {
22252
          var
22253
            calculations = module.get.elementCalculations(),
22254
            callback     = newCallback || settings.onPassingReverse,
22255
            callbackName = 'passingReverse'
22256
          ;
22257
          if(newCallback) {
22258
            module.debug('Adding callback for passing reverse', newCallback);
22259
            settings.onPassingReverse = newCallback;
22260
          }
22261
          if(!calculations.passing) {
22262
            if(module.get.occurred('passing')) {
22263
              module.execute(callback, callbackName);
22264
            }
22265
          }
22266
          else if(!settings.once) {
22267
            module.remove.occurred(callbackName);
22268
          }
22269
          if(newCallback !== undefined) {
22270
            return !calculations.passing;
22271
          }
22272
        },
22273
22274
22275
        topVisibleReverse: function(newCallback) {
22276
          var
22277
            calculations = module.get.elementCalculations(),
22278
            callback     = newCallback || settings.onTopVisibleReverse,
22279
            callbackName = 'topVisibleReverse'
22280
          ;
22281
          if(newCallback) {
22282
            module.debug('Adding callback for top visible reverse', newCallback);
22283
            settings.onTopVisibleReverse = newCallback;
22284
          }
22285
          if(!calculations.topVisible) {
22286
            if(module.get.occurred('topVisible')) {
22287
              module.execute(callback, callbackName);
22288
            }
22289
          }
22290
          else if(!settings.once) {
22291
            module.remove.occurred(callbackName);
22292
          }
22293
          if(newCallback === undefined) {
22294
            return !calculations.topVisible;
22295
          }
22296
        },
22297
22298
        bottomVisibleReverse: function(newCallback) {
22299
          var
22300
            calculations = module.get.elementCalculations(),
22301
            callback     = newCallback || settings.onBottomVisibleReverse,
22302
            callbackName = 'bottomVisibleReverse'
22303
          ;
22304
          if(newCallback) {
22305
            module.debug('Adding callback for bottom visible reverse', newCallback);
22306
            settings.onBottomVisibleReverse = newCallback;
22307
          }
22308
          if(!calculations.bottomVisible) {
22309
            if(module.get.occurred('bottomVisible')) {
22310
              module.execute(callback, callbackName);
22311
            }
22312
          }
22313
          else if(!settings.once) {
22314
            module.remove.occurred(callbackName);
22315
          }
22316
          if(newCallback === undefined) {
22317
            return !calculations.bottomVisible;
22318
          }
22319
        },
22320
22321
        topPassedReverse: function(newCallback) {
22322
          var
22323
            calculations = module.get.elementCalculations(),
22324
            callback     = newCallback || settings.onTopPassedReverse,
22325
            callbackName = 'topPassedReverse'
22326
          ;
22327
          if(newCallback) {
22328
            module.debug('Adding callback for top passed reverse', newCallback);
22329
            settings.onTopPassedReverse = newCallback;
22330
          }
22331
          if(!calculations.topPassed) {
22332
            if(module.get.occurred('topPassed')) {
22333
              module.execute(callback, callbackName);
22334
            }
22335
          }
22336
          else if(!settings.once) {
22337
            module.remove.occurred(callbackName);
22338
          }
22339
          if(newCallback === undefined) {
22340
            return !calculations.onTopPassed;
22341
          }
22342
        },
22343
22344
        bottomPassedReverse: function(newCallback) {
22345
          var
22346
            calculations = module.get.elementCalculations(),
22347
            callback     = newCallback || settings.onBottomPassedReverse,
22348
            callbackName = 'bottomPassedReverse'
22349
          ;
22350
          if(newCallback) {
22351
            module.debug('Adding callback for bottom passed reverse', newCallback);
22352
            settings.onBottomPassedReverse = newCallback;
22353
          }
22354
          if(!calculations.bottomPassed) {
22355
            if(module.get.occurred('bottomPassed')) {
22356
              module.execute(callback, callbackName);
22357
            }
22358
          }
22359
          else if(!settings.once) {
22360
            module.remove.occurred(callbackName);
22361
          }
22362
          if(newCallback === undefined) {
22363
            return !calculations.bottomPassed;
22364
          }
22365
        },
22366
22367
        execute: function(callback, callbackName) {
22368
          var
22369
            calculations = module.get.elementCalculations(),
22370
            screen       = module.get.screenCalculations()
22371
          ;
22372
          callback = callback || false;
22373
          if(callback) {
22374
            if(settings.continuous) {
22375
              module.debug('Callback being called continuously', callbackName, calculations);
22376
              callback.call(element, calculations, screen);
22377
            }
22378
            else if(!module.get.occurred(callbackName)) {
22379
              module.debug('Conditions met', callbackName, calculations);
22380
              callback.call(element, calculations, screen);
22381
            }
22382
          }
22383
          module.save.occurred(callbackName);
22384
        },
22385
22386
        remove: {
22387
          fixed: function() {
22388
            module.debug('Removing fixed position');
22389
            $module
22390
              .removeClass(className.fixed)
22391
              .css({
22392
                position : '',
22393
                top      : '',
22394
                left     : '',
22395
                zIndex   : ''
22396
              })
22397
            ;
22398
            settings.onUnfixed.call(element);
22399
          },
22400
          placeholder: function() {
22401
            module.debug('Removing placeholder content');
22402
            if($placeholder) {
22403
              $placeholder.remove();
22404
            }
22405
          },
22406
          occurred: function(callback) {
22407
            if(callback) {
22408
              var
22409
                occurred = module.cache.occurred
22410
              ;
22411
              if(occurred[callback] !== undefined && occurred[callback] === true) {
22412
                module.debug('Callback can now be called again', callback);
22413
                module.cache.occurred[callback] = false;
22414
              }
22415
            }
22416
            else {
22417
              module.cache.occurred = {};
22418
            }
22419
          }
22420
        },
22421
22422
        save: {
22423
          calculations: function() {
22424
            module.verbose('Saving all calculations necessary to determine positioning');
22425
            module.save.direction();
22426
            module.save.screenCalculations();
22427
            module.save.elementCalculations();
22428
          },
22429
          occurred: function(callback) {
22430
            if(callback) {
22431
              if(module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) {
22432
                module.verbose('Saving callback occurred', callback);
22433
                module.cache.occurred[callback] = true;
22434
              }
22435
            }
22436
          },
22437
          scroll: function(scrollPosition) {
22438
            scrollPosition      = scrollPosition + settings.offset || $context.scrollTop() + settings.offset;
22439
            module.cache.scroll = scrollPosition;
22440
          },
22441
          direction: function() {
22442
            var
22443
              scroll     = module.get.scroll(),
22444
              lastScroll = module.get.lastScroll(),
22445
              direction
22446
            ;
22447
            if(scroll > lastScroll && lastScroll) {
22448
              direction = 'down';
22449
            }
22450
            else if(scroll < lastScroll && lastScroll) {
22451
              direction = 'up';
22452
            }
22453
            else {
22454
              direction = 'static';
22455
            }
22456
            module.cache.direction = direction;
22457
            return module.cache.direction;
22458
          },
22459
          elementPosition: function() {
22460
            var
22461
              element = module.cache.element,
22462
              screen  = module.get.screenSize()
22463
            ;
22464
            module.verbose('Saving element position');
22465
            // (quicker than $.extend)
22466
            element.fits          = (element.height < screen.height);
22467
            element.offset        = $module.offset();
22468
            element.width         = $module.outerWidth();
22469
            element.height        = $module.outerHeight();
22470
            // compensate for scroll in context
22471
            if(module.is.verticallyScrollableContext()) {
22472
              element.offset.top += $context.scrollTop() - $context.offset().top;
22473
            }
22474
            if(module.is.horizontallyScrollableContext()) {
22475
              element.offset.left += $context.scrollLeft - $context.offset().left;
22476
            }
22477
            // store
22478
            module.cache.element = element;
22479
            return element;
22480
          },
22481
          elementCalculations: function() {
22482
            var
22483
              screen     = module.get.screenCalculations(),
22484
              element    = module.get.elementPosition()
22485
            ;
22486
            // offset
22487
            if(settings.includeMargin) {
22488
              element.margin        = {};
22489
              element.margin.top    = parseInt($module.css('margin-top'), 10);
22490
              element.margin.bottom = parseInt($module.css('margin-bottom'), 10);
22491
              element.top    = element.offset.top - element.margin.top;
22492
              element.bottom = element.offset.top + element.height + element.margin.bottom;
22493
            }
22494
            else {
22495
              element.top    = element.offset.top;
22496
              element.bottom = element.offset.top + element.height;
22497
            }
22498
22499
            // visibility
22500
            element.topPassed        = (screen.top >= element.top);
22501
            element.bottomPassed     = (screen.top >= element.bottom);
22502
            element.topVisible       = (screen.bottom >= element.top) && !element.bottomPassed;
22503
            element.bottomVisible    = (screen.bottom >= element.bottom) && !element.topPassed;
22504
            element.pixelsPassed     = 0;
22505
            element.percentagePassed = 0;
22506
22507
            // meta calculations
22508
            element.onScreen  = (element.topVisible && !element.bottomPassed);
22509
            element.passing   = (element.topPassed && !element.bottomPassed);
22510
            element.offScreen = (!element.onScreen);
22511
22512
            // passing calculations
22513
            if(element.passing) {
22514
              element.pixelsPassed     = (screen.top - element.top);
22515
              element.percentagePassed = (screen.top - element.top) / element.height;
22516
            }
22517
            module.cache.element = element;
22518
            module.verbose('Updated element calculations', element);
22519
            return element;
22520
          },
22521
          screenCalculations: function() {
22522
            var
22523
              scroll = module.get.scroll()
22524
            ;
22525
            module.save.direction();
22526
            module.cache.screen.top    = scroll;
22527
            module.cache.screen.bottom = scroll + module.cache.screen.height;
22528
            return module.cache.screen;
22529
          },
22530
          screenSize: function() {
22531
            module.verbose('Saving window position');
22532
            module.cache.screen = {
22533
              height: $context.height()
22534
            };
22535
          },
22536
          position: function() {
22537
            module.save.screenSize();
22538
            module.save.elementPosition();
22539
          }
22540
        },
22541
22542
        get: {
22543
          pixelsPassed: function(amount) {
22544
            var
22545
              element = module.get.elementCalculations()
22546
            ;
22547
            if(amount.search('%') > -1) {
22548
              return ( element.height * (parseInt(amount, 10) / 100) );
22549
            }
22550
            return parseInt(amount, 10);
22551
          },
22552
          occurred: function(callback) {
22553
            return (module.cache.occurred !== undefined)
22554
              ? module.cache.occurred[callback] || false
22555
              : false
22556
            ;
22557
          },
22558
          direction: function() {
22559
            if(module.cache.direction === undefined) {
22560
              module.save.direction();
22561
            }
22562
            return module.cache.direction;
22563
          },
22564
          elementPosition: function() {
22565
            if(module.cache.element === undefined) {
22566
              module.save.elementPosition();
22567
            }
22568
            return module.cache.element;
22569
          },
22570
          elementCalculations: function() {
22571
            if(module.cache.element === undefined) {
22572
              module.save.elementCalculations();
22573
            }
22574
            return module.cache.element;
22575
          },
22576
          screenCalculations: function() {
22577
            if(module.cache.screen === undefined) {
22578
              module.save.screenCalculations();
22579
            }
22580
            return module.cache.screen;
22581
          },
22582
          screenSize: function() {
22583
            if(module.cache.screen === undefined) {
22584
              module.save.screenSize();
22585
            }
22586
            return module.cache.screen;
22587
          },
22588
          scroll: function() {
22589
            if(module.cache.scroll === undefined) {
22590
              module.save.scroll();
22591
            }
22592
            return module.cache.scroll;
22593
          },
22594
          lastScroll: function() {
22595
            if(module.cache.screen === undefined) {
22596
              module.debug('First scroll event, no last scroll could be found');
22597
              return false;
22598
            }
22599
            return module.cache.screen.top;
22600
          }
22601
        },
22602
22603
        setting: function(name, value) {
22604
          if( $.isPlainObject(name) ) {
22605
            $.extend(true, settings, name);
22606
          }
22607
          else if(value !== undefined) {
22608
            settings[name] = value;
22609
          }
22610
          else {
22611
            return settings[name];
22612
          }
22613
        },
22614
        internal: function(name, value) {
22615
          if( $.isPlainObject(name) ) {
22616
            $.extend(true, module, name);
22617
          }
22618
          else if(value !== undefined) {
22619
            module[name] = value;
22620
          }
22621
          else {
22622
            return module[name];
22623
          }
22624
        },
22625
        debug: function() {
22626
          if(!settings.silent && settings.debug) {
22627
            if(settings.performance) {
22628
              module.performance.log(arguments);
22629
            }
22630
            else {
22631
              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
22632
              module.debug.apply(console, arguments);
22633
            }
22634
          }
22635
        },
22636
        verbose: function() {
22637
          if(!settings.silent && settings.verbose && settings.debug) {
22638
            if(settings.performance) {
22639
              module.performance.log(arguments);
22640
            }
22641
            else {
22642
              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
22643
              module.verbose.apply(console, arguments);
22644
            }
22645
          }
22646
        },
22647
        error: function() {
22648
          if(!settings.silent) {
22649
            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
22650
            module.error.apply(console, arguments);
22651
          }
22652
        },
22653
        performance: {
22654
          log: function(message) {
22655
            var
22656
              currentTime,
22657
              executionTime,
22658
              previousTime
22659
            ;
22660
            if(settings.performance) {
22661
              currentTime   = new Date().getTime();
22662
              previousTime  = time || currentTime;
22663
              executionTime = currentTime - previousTime;
22664
              time          = currentTime;
22665
              performance.push({
22666
                'Name'           : message[0],
22667
                'Arguments'      : [].slice.call(message, 1) || '',
22668
                'Element'        : element,
22669
                'Execution Time' : executionTime
22670
              });
22671
            }
22672
            clearTimeout(module.performance.timer);
22673
            module.performance.timer = setTimeout(module.performance.display, 500);
22674
          },
22675
          display: function() {
22676
            var
22677
              title = settings.name + ':',
22678
              totalTime = 0
22679
            ;
22680
            time = false;
22681
            clearTimeout(module.performance.timer);
22682
            $.each(performance, function(index, data) {
22683
              totalTime += data['Execution Time'];
22684
            });
22685
            title += ' ' + totalTime + 'ms';
22686
            if(moduleSelector) {
22687
              title += ' \'' + moduleSelector + '\'';
22688
            }
22689
            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
22690
              console.groupCollapsed(title);
22691
              if(console.table) {
22692
                console.table(performance);
22693
              }
22694
              else {
22695
                $.each(performance, function(index, data) {
22696
                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
22697
                });
22698
              }
22699
              console.groupEnd();
22700
            }
22701
            performance = [];
22702
          }
22703
        },
22704
        invoke: function(query, passedArguments, context) {
22705
          var
22706
            object = instance,
22707
            maxDepth,
22708
            found,
22709
            response
22710
          ;
22711
          passedArguments = passedArguments || queryArguments;
22712
          context         = element         || context;
22713
          if(typeof query == 'string' && object !== undefined) {
22714
            query    = query.split(/[\. ]/);
22715
            maxDepth = query.length - 1;
22716
            $.each(query, function(depth, value) {
22717
              var camelCaseValue = (depth != maxDepth)
22718
                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
22719
                : query
22720
              ;
22721
              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
22722
                object = object[camelCaseValue];
22723
              }
22724
              else if( object[camelCaseValue] !== undefined ) {
22725
                found = object[camelCaseValue];
22726
                return false;
22727
              }
22728
              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
22729
                object = object[value];
22730
              }
22731
              else if( object[value] !== undefined ) {
22732
                found = object[value];
22733
                return false;
22734
              }
22735
              else {
22736
                module.error(error.method, query);
22737
                return false;
22738
              }
22739
            });
22740
          }
22741
          if ( $.isFunction( found ) ) {
22742
            response = found.apply(context, passedArguments);
22743
          }
22744
          else if(found !== undefined) {
22745
            response = found;
22746
          }
22747
          if($.isArray(returnedValue)) {
22748
            returnedValue.push(response);
22749
          }
22750
          else if(returnedValue !== undefined) {
22751
            returnedValue = [returnedValue, response];
22752
          }
22753
          else if(response !== undefined) {
22754
            returnedValue = response;
22755
          }
22756
          return found;
22757
        }
22758
      };
22759
22760
      if(methodInvoked) {
22761
        if(instance === undefined) {
22762
          module.initialize();
22763
        }
22764
        instance.save.scroll();
22765
        instance.save.calculations();
22766
        module.invoke(query);
22767
      }
22768
      else {
22769
        if(instance !== undefined) {
22770
          instance.invoke('destroy');
22771
        }
22772
        module.initialize();
22773
      }
22774
    })
22775
  ;
22776
22777
  return (returnedValue !== undefined)
22778
    ? returnedValue
22779
    : this
22780
  ;
22781
};
22782
22783
$.fn.visibility.settings = {
22784
22785
  name                   : 'Visibility',
22786
  namespace              : 'visibility',
22787
22788
  debug                  : false,
22789
  verbose                : false,
22790
  performance            : true,
22791
22792
  // whether to use mutation observers to follow changes
22793
  observeChanges         : true,
22794
22795
  // check position immediately on init
22796
  initialCheck           : true,
22797
22798
  // whether to refresh calculations after all page images load
22799
  refreshOnLoad          : true,
22800
22801
  // whether to refresh calculations after page resize event
22802
  refreshOnResize        : true,
22803
22804
  // should call callbacks on refresh event (resize, etc)
22805
  checkOnRefresh         : true,
22806
22807
  // callback should only occur one time
22808
  once                   : true,
22809
22810
  // callback should fire continuously whe evaluates to true
22811
  continuous             : false,
22812
22813
  // offset to use with scroll top
22814
  offset                 : 0,
22815
22816
  // whether to include margin in elements position
22817
  includeMargin          : false,
22818
22819
  // scroll context for visibility checks
22820
  context                : window,
22821
22822
  // visibility check delay in ms (defaults to animationFrame)
22823
  throttle               : false,
22824
22825
  // special visibility type (image, fixed)
22826
  type                   : false,
22827
22828
  // z-index to use with visibility 'fixed'
22829
  zIndex                 : '10',
22830
22831
  // image only animation settings
22832
  transition             : 'fade in',
22833
  duration               : 1000,
22834
22835
  // array of callbacks for percentage
22836
  onPassed               : {},
22837
22838
  // standard callbacks
22839
  onOnScreen             : false,
22840
  onOffScreen            : false,
22841
  onPassing              : false,
22842
  onTopVisible           : false,
22843
  onBottomVisible        : false,
22844
  onTopPassed            : false,
22845
  onBottomPassed         : false,
22846
22847
  // reverse callbacks
22848
  onPassingReverse       : false,
22849
  onTopVisibleReverse    : false,
22850
  onBottomVisibleReverse : false,
22851
  onTopPassedReverse     : false,
22852
  onBottomPassedReverse  : false,
22853
22854
  // special callbacks for image
22855
  onLoad                 : function() {},
22856
  onAllLoaded            : function() {},
22857
22858
  // special callbacks for fixed position
22859
  onFixed                : function() {},
22860
  onUnfixed              : function() {},
22861
22862
  // utility callbacks
22863
  onUpdate               : false, // disabled by default for performance
22864
  onRefresh              : function(){},
22865
22866
  metadata : {
22867
    src: 'src'
22868
  },
22869
22870
  className: {
22871
    fixed       : 'fixed',
22872
    placeholder : 'placeholder',
22873
    visible     : 'visible'
22874
  },
22875
22876
  error : {
22877
    method  : 'The method you called is not defined.',
22878
    visible : 'Element is hidden, you must call refresh after element becomes visible'
22879
  }
22880
22881
};
22882
22883
})( jQuery, window, document );
22884

public/lib/semantic/components/visibility.js 1 location

@@ 11-1311 (lines=1301) @@
8
 *
9
 */
10
11
;(function ($, window, document, undefined) {
12
13
"use strict";
14
15
window = (typeof window != 'undefined' && window.Math == Math)
16
  ? window
17
  : (typeof self != 'undefined' && self.Math == Math)
18
    ? self
19
    : Function('return this')()
20
;
21
22
$.fn.visibility = function(parameters) {
23
  var
24
    $allModules    = $(this),
25
    moduleSelector = $allModules.selector || '',
26
27
    time           = new Date().getTime(),
28
    performance    = [],
29
30
    query          = arguments[0],
31
    methodInvoked  = (typeof query == 'string'),
32
    queryArguments = [].slice.call(arguments, 1),
33
    returnedValue,
34
35
    moduleCount    = $allModules.length,
36
    loadedCount    = 0
37
  ;
38
39
  $allModules
40
    .each(function() {
41
      var
42
        settings        = ( $.isPlainObject(parameters) )
43
          ? $.extend(true, {}, $.fn.visibility.settings, parameters)
44
          : $.extend({}, $.fn.visibility.settings),
45
46
        className       = settings.className,
47
        namespace       = settings.namespace,
48
        error           = settings.error,
49
        metadata        = settings.metadata,
50
51
        eventNamespace  = '.' + namespace,
52
        moduleNamespace = 'module-' + namespace,
53
54
        $window         = $(window),
55
56
        $module         = $(this),
57
        $context        = $(settings.context),
58
59
        $placeholder,
60
61
        selector        = $module.selector || '',
62
        instance        = $module.data(moduleNamespace),
63
64
        requestAnimationFrame = window.requestAnimationFrame
65
          || window.mozRequestAnimationFrame
66
          || window.webkitRequestAnimationFrame
67
          || window.msRequestAnimationFrame
68
          || function(callback) { setTimeout(callback, 0); },
69
70
        element         = this,
71
        disabled        = false,
72
73
        contextObserver,
74
        observer,
75
        module
76
      ;
77
78
      module = {
79
80
        initialize: function() {
81
          module.debug('Initializing', settings);
82
83
          module.setup.cache();
84
85
          if( module.should.trackChanges() ) {
86
87
            if(settings.type == 'image') {
88
              module.setup.image();
89
            }
90
            if(settings.type == 'fixed') {
91
              module.setup.fixed();
92
            }
93
94
            if(settings.observeChanges) {
95
              module.observeChanges();
96
            }
97
            module.bind.events();
98
          }
99
100
          module.save.position();
101
          if( !module.is.visible() ) {
102
            module.error(error.visible, $module);
103
          }
104
105
          if(settings.initialCheck) {
106
            module.checkVisibility();
107
          }
108
          module.instantiate();
109
        },
110
111
        instantiate: function() {
112
          module.debug('Storing instance', module);
113
          $module
114
            .data(moduleNamespace, module)
115
          ;
116
          instance = module;
117
        },
118
119
        destroy: function() {
120
          module.verbose('Destroying previous module');
121
          if(observer) {
122
            observer.disconnect();
123
          }
124
          if(contextObserver) {
125
            contextObserver.disconnect();
126
          }
127
          $window
128
            .off('load'   + eventNamespace, module.event.load)
129
            .off('resize' + eventNamespace, module.event.resize)
130
          ;
131
          $context
132
            .off('scroll'       + eventNamespace, module.event.scroll)
133
            .off('scrollchange' + eventNamespace, module.event.scrollchange)
134
          ;
135
          if(settings.type == 'fixed') {
136
            module.resetFixed();
137
            module.remove.placeholder();
138
          }
139
          $module
140
            .off(eventNamespace)
141
            .removeData(moduleNamespace)
142
          ;
143
        },
144
145
        observeChanges: function() {
146
          if('MutationObserver' in window) {
147
            contextObserver = new MutationObserver(module.event.contextChanged);
148
            observer        = new MutationObserver(module.event.changed);
149
            contextObserver.observe(document, {
150
              childList : true,
151
              subtree   : true
152
            });
153
            observer.observe(element, {
154
              childList : true,
155
              subtree   : true
156
            });
157
            module.debug('Setting up mutation observer', observer);
158
          }
159
        },
160
161
        bind: {
162
          events: function() {
163
            module.verbose('Binding visibility events to scroll and resize');
164
            if(settings.refreshOnLoad) {
165
              $window
166
                .on('load'   + eventNamespace, module.event.load)
167
              ;
168
            }
169
            $window
170
              .on('resize' + eventNamespace, module.event.resize)
171
            ;
172
            // pub/sub pattern
173
            $context
174
              .off('scroll'      + eventNamespace)
175
              .on('scroll'       + eventNamespace, module.event.scroll)
176
              .on('scrollchange' + eventNamespace, module.event.scrollchange)
177
            ;
178
          }
179
        },
180
181
        event: {
182
          changed: function(mutations) {
183
            module.verbose('DOM tree modified, updating visibility calculations');
184
            module.timer = setTimeout(function() {
185
              module.verbose('DOM tree modified, updating sticky menu');
186
              module.refresh();
187
            }, 100);
188
          },
189
          contextChanged: function(mutations) {
190
            [].forEach.call(mutations, function(mutation) {
191
              if(mutation.removedNodes) {
192
                [].forEach.call(mutation.removedNodes, function(node) {
193
                  if(node == element || $(node).find(element).length > 0) {
194
                    module.debug('Element removed from DOM, tearing down events');
195
                    module.destroy();
196
                  }
197
                });
198
              }
199
            });
200
          },
201
          resize: function() {
202
            module.debug('Window resized');
203
            if(settings.refreshOnResize) {
204
              requestAnimationFrame(module.refresh);
205
            }
206
          },
207
          load: function() {
208
            module.debug('Page finished loading');
209
            requestAnimationFrame(module.refresh);
210
          },
211
          // publishes scrollchange event on one scroll
212
          scroll: function() {
213
            if(settings.throttle) {
214
              clearTimeout(module.timer);
215
              module.timer = setTimeout(function() {
216
                $context.triggerHandler('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
217
              }, settings.throttle);
218
            }
219
            else {
220
              requestAnimationFrame(function() {
221
                $context.triggerHandler('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
222
              });
223
            }
224
          },
225
          // subscribes to scrollchange
226
          scrollchange: function(event, scrollPosition) {
227
            module.checkVisibility(scrollPosition);
228
          },
229
        },
230
231
        precache: function(images, callback) {
232
          if (!(images instanceof Array)) {
233
            images = [images];
234
          }
235
          var
236
            imagesLength  = images.length,
237
            loadedCounter = 0,
238
            cache         = [],
239
            cacheImage    = document.createElement('img'),
240
            handleLoad    = function() {
241
              loadedCounter++;
242
              if (loadedCounter >= images.length) {
243
                if ($.isFunction(callback)) {
244
                  callback();
245
                }
246
              }
247
            }
248
          ;
249
          while (imagesLength--) {
250
            cacheImage         = document.createElement('img');
251
            cacheImage.onload  = handleLoad;
252
            cacheImage.onerror = handleLoad;
253
            cacheImage.src     = images[imagesLength];
254
            cache.push(cacheImage);
255
          }
256
        },
257
258
        enableCallbacks: function() {
259
          module.debug('Allowing callbacks to occur');
260
          disabled = false;
261
        },
262
263
        disableCallbacks: function() {
264
          module.debug('Disabling all callbacks temporarily');
265
          disabled = true;
266
        },
267
268
        should: {
269
          trackChanges: function() {
270
            if(methodInvoked) {
271
              module.debug('One time query, no need to bind events');
272
              return false;
273
            }
274
            module.debug('Callbacks being attached');
275
            return true;
276
          }
277
        },
278
279
        setup: {
280
          cache: function() {
281
            module.cache = {
282
              occurred : {},
283
              screen   : {},
284
              element  : {},
285
            };
286
          },
287
          image: function() {
288
            var
289
              src = $module.data(metadata.src)
290
            ;
291
            if(src) {
292
              module.verbose('Lazy loading image', src);
293
              settings.once           = true;
294
              settings.observeChanges = false;
295
296
              // show when top visible
297
              settings.onOnScreen = function() {
298
                module.debug('Image on screen', element);
299
                module.precache(src, function() {
300
                  module.set.image(src, function() {
301
                    loadedCount++;
302
                    if(loadedCount == moduleCount) {
303
                      settings.onAllLoaded.call(this);
304
                    }
305
                    settings.onLoad.call(this);
306
                  });
307
                });
308
              };
309
            }
310
          },
311
          fixed: function() {
312
            module.debug('Setting up fixed');
313
            settings.once           = false;
314
            settings.observeChanges = false;
315
            settings.initialCheck   = true;
316
            settings.refreshOnLoad  = true;
317
            if(!parameters.transition) {
318
              settings.transition = false;
319
            }
320
            module.create.placeholder();
321
            module.debug('Added placeholder', $placeholder);
322
            settings.onTopPassed = function() {
323
              module.debug('Element passed, adding fixed position', $module);
324
              module.show.placeholder();
325
              module.set.fixed();
326
              if(settings.transition) {
327
                if($.fn.transition !== undefined) {
328
                  $module.transition(settings.transition, settings.duration);
329
                }
330
              }
331
            };
332
            settings.onTopPassedReverse = function() {
333
              module.debug('Element returned to position, removing fixed', $module);
334
              module.hide.placeholder();
335
              module.remove.fixed();
336
            };
337
          }
338
        },
339
340
        create: {
341
          placeholder: function() {
342
            module.verbose('Creating fixed position placeholder');
343
            $placeholder = $module
344
              .clone(false)
345
              .css('display', 'none')
346
              .addClass(className.placeholder)
347
              .insertAfter($module)
348
            ;
349
          }
350
        },
351
352
        show: {
353
          placeholder: function() {
354
            module.verbose('Showing placeholder');
355
            $placeholder
356
              .css('display', 'block')
357
              .css('visibility', 'hidden')
358
            ;
359
          }
360
        },
361
        hide: {
362
          placeholder: function() {
363
            module.verbose('Hiding placeholder');
364
            $placeholder
365
              .css('display', 'none')
366
              .css('visibility', '')
367
            ;
368
          }
369
        },
370
371
        set: {
372
          fixed: function() {
373
            module.verbose('Setting element to fixed position');
374
            $module
375
              .addClass(className.fixed)
376
              .css({
377
                position : 'fixed',
378
                top      : settings.offset + 'px',
379
                left     : 'auto',
380
                zIndex   : settings.zIndex
381
              })
382
            ;
383
            settings.onFixed.call(element);
384
          },
385
          image: function(src, callback) {
386
            $module
387
              .attr('src', src)
388
            ;
389
            if(settings.transition) {
390
              if( $.fn.transition !== undefined) {
391
                if($module.hasClass(className.visible)) {
392
                  module.debug('Transition already occurred on this image, skipping animation');
393
                  return;
394
                }
395
                $module.transition(settings.transition, settings.duration, callback);
396
              }
397
              else {
398
                $module.fadeIn(settings.duration, callback);
399
              }
400
            }
401
            else {
402
              $module.show();
403
            }
404
          }
405
        },
406
407
        is: {
408
          onScreen: function() {
409
            var
410
              calculations   = module.get.elementCalculations()
411
            ;
412
            return calculations.onScreen;
413
          },
414
          offScreen: function() {
415
            var
416
              calculations   = module.get.elementCalculations()
417
            ;
418
            return calculations.offScreen;
419
          },
420
          visible: function() {
421
            if(module.cache && module.cache.element) {
422
              return !(module.cache.element.width === 0 && module.cache.element.offset.top === 0);
423
            }
424
            return false;
425
          },
426
          verticallyScrollableContext: function() {
427
            var
428
              overflowY = ($context.get(0) !== window)
429
                ? $context.css('overflow-y')
430
                : false
431
            ;
432
            return (overflowY == 'auto' || overflowY == 'scroll');
433
          },
434
          horizontallyScrollableContext: function() {
435
            var
436
              overflowX = ($context.get(0) !== window)
437
                ? $context.css('overflow-x')
438
                : false
439
            ;
440
            return (overflowX == 'auto' || overflowX == 'scroll');
441
          }
442
        },
443
444
        refresh: function() {
445
          module.debug('Refreshing constants (width/height)');
446
          if(settings.type == 'fixed') {
447
            module.resetFixed();
448
          }
449
          module.reset();
450
          module.save.position();
451
          if(settings.checkOnRefresh) {
452
            module.checkVisibility();
453
          }
454
          settings.onRefresh.call(element);
455
        },
456
457
        resetFixed: function () {
458
          module.remove.fixed();
459
          module.remove.occurred();
460
        },
461
462
        reset: function() {
463
          module.verbose('Resetting all cached values');
464
          if( $.isPlainObject(module.cache) ) {
465
            module.cache.screen = {};
466
            module.cache.element = {};
467
          }
468
        },
469
470
        checkVisibility: function(scroll) {
471
          module.verbose('Checking visibility of element', module.cache.element);
472
473
          if( !disabled && module.is.visible() ) {
474
475
            // save scroll position
476
            module.save.scroll(scroll);
477
478
            // update calculations derived from scroll
479
            module.save.calculations();
480
481
            // percentage
482
            module.passed();
483
484
            // reverse (must be first)
485
            module.passingReverse();
486
            module.topVisibleReverse();
487
            module.bottomVisibleReverse();
488
            module.topPassedReverse();
489
            module.bottomPassedReverse();
490
491
            // one time
492
            module.onScreen();
493
            module.offScreen();
494
            module.passing();
495
            module.topVisible();
496
            module.bottomVisible();
497
            module.topPassed();
498
            module.bottomPassed();
499
500
            // on update callback
501
            if(settings.onUpdate) {
502
              settings.onUpdate.call(element, module.get.elementCalculations());
503
            }
504
          }
505
        },
506
507
        passed: function(amount, newCallback) {
508
          var
509
            calculations   = module.get.elementCalculations(),
510
            amountInPixels
511
          ;
512
          // assign callback
513
          if(amount && newCallback) {
514
            settings.onPassed[amount] = newCallback;
515
          }
516
          else if(amount !== undefined) {
517
            return (module.get.pixelsPassed(amount) > calculations.pixelsPassed);
518
          }
519
          else if(calculations.passing) {
520
            $.each(settings.onPassed, function(amount, callback) {
521
              if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
522
                module.execute(callback, amount);
523
              }
524
              else if(!settings.once) {
525
                module.remove.occurred(callback);
526
              }
527
            });
528
          }
529
        },
530
531
        onScreen: function(newCallback) {
532
          var
533
            calculations = module.get.elementCalculations(),
534
            callback     = newCallback || settings.onOnScreen,
535
            callbackName = 'onScreen'
536
          ;
537
          if(newCallback) {
538
            module.debug('Adding callback for onScreen', newCallback);
539
            settings.onOnScreen = newCallback;
540
          }
541
          if(calculations.onScreen) {
542
            module.execute(callback, callbackName);
543
          }
544
          else if(!settings.once) {
545
            module.remove.occurred(callbackName);
546
          }
547
          if(newCallback !== undefined) {
548
            return calculations.onOnScreen;
549
          }
550
        },
551
552
        offScreen: function(newCallback) {
553
          var
554
            calculations = module.get.elementCalculations(),
555
            callback     = newCallback || settings.onOffScreen,
556
            callbackName = 'offScreen'
557
          ;
558
          if(newCallback) {
559
            module.debug('Adding callback for offScreen', newCallback);
560
            settings.onOffScreen = newCallback;
561
          }
562
          if(calculations.offScreen) {
563
            module.execute(callback, callbackName);
564
          }
565
          else if(!settings.once) {
566
            module.remove.occurred(callbackName);
567
          }
568
          if(newCallback !== undefined) {
569
            return calculations.onOffScreen;
570
          }
571
        },
572
573
        passing: function(newCallback) {
574
          var
575
            calculations = module.get.elementCalculations(),
576
            callback     = newCallback || settings.onPassing,
577
            callbackName = 'passing'
578
          ;
579
          if(newCallback) {
580
            module.debug('Adding callback for passing', newCallback);
581
            settings.onPassing = newCallback;
582
          }
583
          if(calculations.passing) {
584
            module.execute(callback, callbackName);
585
          }
586
          else if(!settings.once) {
587
            module.remove.occurred(callbackName);
588
          }
589
          if(newCallback !== undefined) {
590
            return calculations.passing;
591
          }
592
        },
593
594
595
        topVisible: function(newCallback) {
596
          var
597
            calculations = module.get.elementCalculations(),
598
            callback     = newCallback || settings.onTopVisible,
599
            callbackName = 'topVisible'
600
          ;
601
          if(newCallback) {
602
            module.debug('Adding callback for top visible', newCallback);
603
            settings.onTopVisible = newCallback;
604
          }
605
          if(calculations.topVisible) {
606
            module.execute(callback, callbackName);
607
          }
608
          else if(!settings.once) {
609
            module.remove.occurred(callbackName);
610
          }
611
          if(newCallback === undefined) {
612
            return calculations.topVisible;
613
          }
614
        },
615
616
        bottomVisible: function(newCallback) {
617
          var
618
            calculations = module.get.elementCalculations(),
619
            callback     = newCallback || settings.onBottomVisible,
620
            callbackName = 'bottomVisible'
621
          ;
622
          if(newCallback) {
623
            module.debug('Adding callback for bottom visible', newCallback);
624
            settings.onBottomVisible = newCallback;
625
          }
626
          if(calculations.bottomVisible) {
627
            module.execute(callback, callbackName);
628
          }
629
          else if(!settings.once) {
630
            module.remove.occurred(callbackName);
631
          }
632
          if(newCallback === undefined) {
633
            return calculations.bottomVisible;
634
          }
635
        },
636
637
        topPassed: function(newCallback) {
638
          var
639
            calculations = module.get.elementCalculations(),
640
            callback     = newCallback || settings.onTopPassed,
641
            callbackName = 'topPassed'
642
          ;
643
          if(newCallback) {
644
            module.debug('Adding callback for top passed', newCallback);
645
            settings.onTopPassed = newCallback;
646
          }
647
          if(calculations.topPassed) {
648
            module.execute(callback, callbackName);
649
          }
650
          else if(!settings.once) {
651
            module.remove.occurred(callbackName);
652
          }
653
          if(newCallback === undefined) {
654
            return calculations.topPassed;
655
          }
656
        },
657
658
        bottomPassed: function(newCallback) {
659
          var
660
            calculations = module.get.elementCalculations(),
661
            callback     = newCallback || settings.onBottomPassed,
662
            callbackName = 'bottomPassed'
663
          ;
664
          if(newCallback) {
665
            module.debug('Adding callback for bottom passed', newCallback);
666
            settings.onBottomPassed = newCallback;
667
          }
668
          if(calculations.bottomPassed) {
669
            module.execute(callback, callbackName);
670
          }
671
          else if(!settings.once) {
672
            module.remove.occurred(callbackName);
673
          }
674
          if(newCallback === undefined) {
675
            return calculations.bottomPassed;
676
          }
677
        },
678
679
        passingReverse: function(newCallback) {
680
          var
681
            calculations = module.get.elementCalculations(),
682
            callback     = newCallback || settings.onPassingReverse,
683
            callbackName = 'passingReverse'
684
          ;
685
          if(newCallback) {
686
            module.debug('Adding callback for passing reverse', newCallback);
687
            settings.onPassingReverse = newCallback;
688
          }
689
          if(!calculations.passing) {
690
            if(module.get.occurred('passing')) {
691
              module.execute(callback, callbackName);
692
            }
693
          }
694
          else if(!settings.once) {
695
            module.remove.occurred(callbackName);
696
          }
697
          if(newCallback !== undefined) {
698
            return !calculations.passing;
699
          }
700
        },
701
702
703
        topVisibleReverse: function(newCallback) {
704
          var
705
            calculations = module.get.elementCalculations(),
706
            callback     = newCallback || settings.onTopVisibleReverse,
707
            callbackName = 'topVisibleReverse'
708
          ;
709
          if(newCallback) {
710
            module.debug('Adding callback for top visible reverse', newCallback);
711
            settings.onTopVisibleReverse = newCallback;
712
          }
713
          if(!calculations.topVisible) {
714
            if(module.get.occurred('topVisible')) {
715
              module.execute(callback, callbackName);
716
            }
717
          }
718
          else if(!settings.once) {
719
            module.remove.occurred(callbackName);
720
          }
721
          if(newCallback === undefined) {
722
            return !calculations.topVisible;
723
          }
724
        },
725
726
        bottomVisibleReverse: function(newCallback) {
727
          var
728
            calculations = module.get.elementCalculations(),
729
            callback     = newCallback || settings.onBottomVisibleReverse,
730
            callbackName = 'bottomVisibleReverse'
731
          ;
732
          if(newCallback) {
733
            module.debug('Adding callback for bottom visible reverse', newCallback);
734
            settings.onBottomVisibleReverse = newCallback;
735
          }
736
          if(!calculations.bottomVisible) {
737
            if(module.get.occurred('bottomVisible')) {
738
              module.execute(callback, callbackName);
739
            }
740
          }
741
          else if(!settings.once) {
742
            module.remove.occurred(callbackName);
743
          }
744
          if(newCallback === undefined) {
745
            return !calculations.bottomVisible;
746
          }
747
        },
748
749
        topPassedReverse: function(newCallback) {
750
          var
751
            calculations = module.get.elementCalculations(),
752
            callback     = newCallback || settings.onTopPassedReverse,
753
            callbackName = 'topPassedReverse'
754
          ;
755
          if(newCallback) {
756
            module.debug('Adding callback for top passed reverse', newCallback);
757
            settings.onTopPassedReverse = newCallback;
758
          }
759
          if(!calculations.topPassed) {
760
            if(module.get.occurred('topPassed')) {
761
              module.execute(callback, callbackName);
762
            }
763
          }
764
          else if(!settings.once) {
765
            module.remove.occurred(callbackName);
766
          }
767
          if(newCallback === undefined) {
768
            return !calculations.onTopPassed;
769
          }
770
        },
771
772
        bottomPassedReverse: function(newCallback) {
773
          var
774
            calculations = module.get.elementCalculations(),
775
            callback     = newCallback || settings.onBottomPassedReverse,
776
            callbackName = 'bottomPassedReverse'
777
          ;
778
          if(newCallback) {
779
            module.debug('Adding callback for bottom passed reverse', newCallback);
780
            settings.onBottomPassedReverse = newCallback;
781
          }
782
          if(!calculations.bottomPassed) {
783
            if(module.get.occurred('bottomPassed')) {
784
              module.execute(callback, callbackName);
785
            }
786
          }
787
          else if(!settings.once) {
788
            module.remove.occurred(callbackName);
789
          }
790
          if(newCallback === undefined) {
791
            return !calculations.bottomPassed;
792
          }
793
        },
794
795
        execute: function(callback, callbackName) {
796
          var
797
            calculations = module.get.elementCalculations(),
798
            screen       = module.get.screenCalculations()
799
          ;
800
          callback = callback || false;
801
          if(callback) {
802
            if(settings.continuous) {
803
              module.debug('Callback being called continuously', callbackName, calculations);
804
              callback.call(element, calculations, screen);
805
            }
806
            else if(!module.get.occurred(callbackName)) {
807
              module.debug('Conditions met', callbackName, calculations);
808
              callback.call(element, calculations, screen);
809
            }
810
          }
811
          module.save.occurred(callbackName);
812
        },
813
814
        remove: {
815
          fixed: function() {
816
            module.debug('Removing fixed position');
817
            $module
818
              .removeClass(className.fixed)
819
              .css({
820
                position : '',
821
                top      : '',
822
                left     : '',
823
                zIndex   : ''
824
              })
825
            ;
826
            settings.onUnfixed.call(element);
827
          },
828
          placeholder: function() {
829
            module.debug('Removing placeholder content');
830
            if($placeholder) {
831
              $placeholder.remove();
832
            }
833
          },
834
          occurred: function(callback) {
835
            if(callback) {
836
              var
837
                occurred = module.cache.occurred
838
              ;
839
              if(occurred[callback] !== undefined && occurred[callback] === true) {
840
                module.debug('Callback can now be called again', callback);
841
                module.cache.occurred[callback] = false;
842
              }
843
            }
844
            else {
845
              module.cache.occurred = {};
846
            }
847
          }
848
        },
849
850
        save: {
851
          calculations: function() {
852
            module.verbose('Saving all calculations necessary to determine positioning');
853
            module.save.direction();
854
            module.save.screenCalculations();
855
            module.save.elementCalculations();
856
          },
857
          occurred: function(callback) {
858
            if(callback) {
859
              if(module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) {
860
                module.verbose('Saving callback occurred', callback);
861
                module.cache.occurred[callback] = true;
862
              }
863
            }
864
          },
865
          scroll: function(scrollPosition) {
866
            scrollPosition      = scrollPosition + settings.offset || $context.scrollTop() + settings.offset;
867
            module.cache.scroll = scrollPosition;
868
          },
869
          direction: function() {
870
            var
871
              scroll     = module.get.scroll(),
872
              lastScroll = module.get.lastScroll(),
873
              direction
874
            ;
875
            if(scroll > lastScroll && lastScroll) {
876
              direction = 'down';
877
            }
878
            else if(scroll < lastScroll && lastScroll) {
879
              direction = 'up';
880
            }
881
            else {
882
              direction = 'static';
883
            }
884
            module.cache.direction = direction;
885
            return module.cache.direction;
886
          },
887
          elementPosition: function() {
888
            var
889
              element = module.cache.element,
890
              screen  = module.get.screenSize()
891
            ;
892
            module.verbose('Saving element position');
893
            // (quicker than $.extend)
894
            element.fits          = (element.height < screen.height);
895
            element.offset        = $module.offset();
896
            element.width         = $module.outerWidth();
897
            element.height        = $module.outerHeight();
898
            // compensate for scroll in context
899
            if(module.is.verticallyScrollableContext()) {
900
              element.offset.top += $context.scrollTop() - $context.offset().top;
901
            }
902
            if(module.is.horizontallyScrollableContext()) {
903
              element.offset.left += $context.scrollLeft - $context.offset().left;
904
            }
905
            // store
906
            module.cache.element = element;
907
            return element;
908
          },
909
          elementCalculations: function() {
910
            var
911
              screen     = module.get.screenCalculations(),
912
              element    = module.get.elementPosition()
913
            ;
914
            // offset
915
            if(settings.includeMargin) {
916
              element.margin        = {};
917
              element.margin.top    = parseInt($module.css('margin-top'), 10);
918
              element.margin.bottom = parseInt($module.css('margin-bottom'), 10);
919
              element.top    = element.offset.top - element.margin.top;
920
              element.bottom = element.offset.top + element.height + element.margin.bottom;
921
            }
922
            else {
923
              element.top    = element.offset.top;
924
              element.bottom = element.offset.top + element.height;
925
            }
926
927
            // visibility
928
            element.topPassed        = (screen.top >= element.top);
929
            element.bottomPassed     = (screen.top >= element.bottom);
930
            element.topVisible       = (screen.bottom >= element.top) && !element.bottomPassed;
931
            element.bottomVisible    = (screen.bottom >= element.bottom) && !element.topPassed;
932
            element.pixelsPassed     = 0;
933
            element.percentagePassed = 0;
934
935
            // meta calculations
936
            element.onScreen  = (element.topVisible && !element.bottomPassed);
937
            element.passing   = (element.topPassed && !element.bottomPassed);
938
            element.offScreen = (!element.onScreen);
939
940
            // passing calculations
941
            if(element.passing) {
942
              element.pixelsPassed     = (screen.top - element.top);
943
              element.percentagePassed = (screen.top - element.top) / element.height;
944
            }
945
            module.cache.element = element;
946
            module.verbose('Updated element calculations', element);
947
            return element;
948
          },
949
          screenCalculations: function() {
950
            var
951
              scroll = module.get.scroll()
952
            ;
953
            module.save.direction();
954
            module.cache.screen.top    = scroll;
955
            module.cache.screen.bottom = scroll + module.cache.screen.height;
956
            return module.cache.screen;
957
          },
958
          screenSize: function() {
959
            module.verbose('Saving window position');
960
            module.cache.screen = {
961
              height: $context.height()
962
            };
963
          },
964
          position: function() {
965
            module.save.screenSize();
966
            module.save.elementPosition();
967
          }
968
        },
969
970
        get: {
971
          pixelsPassed: function(amount) {
972
            var
973
              element = module.get.elementCalculations()
974
            ;
975
            if(amount.search('%') > -1) {
976
              return ( element.height * (parseInt(amount, 10) / 100) );
977
            }
978
            return parseInt(amount, 10);
979
          },
980
          occurred: function(callback) {
981
            return (module.cache.occurred !== undefined)
982
              ? module.cache.occurred[callback] || false
983
              : false
984
            ;
985
          },
986
          direction: function() {
987
            if(module.cache.direction === undefined) {
988
              module.save.direction();
989
            }
990
            return module.cache.direction;
991
          },
992
          elementPosition: function() {
993
            if(module.cache.element === undefined) {
994
              module.save.elementPosition();
995
            }
996
            return module.cache.element;
997
          },
998
          elementCalculations: function() {
999
            if(module.cache.element === undefined) {
1000
              module.save.elementCalculations();
1001
            }
1002
            return module.cache.element;
1003
          },
1004
          screenCalculations: function() {
1005
            if(module.cache.screen === undefined) {
1006
              module.save.screenCalculations();
1007
            }
1008
            return module.cache.screen;
1009
          },
1010
          screenSize: function() {
1011
            if(module.cache.screen === undefined) {
1012
              module.save.screenSize();
1013
            }
1014
            return module.cache.screen;
1015
          },
1016
          scroll: function() {
1017
            if(module.cache.scroll === undefined) {
1018
              module.save.scroll();
1019
            }
1020
            return module.cache.scroll;
1021
          },
1022
          lastScroll: function() {
1023
            if(module.cache.screen === undefined) {
1024
              module.debug('First scroll event, no last scroll could be found');
1025
              return false;
1026
            }
1027
            return module.cache.screen.top;
1028
          }
1029
        },
1030
1031
        setting: function(name, value) {
1032
          if( $.isPlainObject(name) ) {
1033
            $.extend(true, settings, name);
1034
          }
1035
          else if(value !== undefined) {
1036
            settings[name] = value;
1037
          }
1038
          else {
1039
            return settings[name];
1040
          }
1041
        },
1042
        internal: function(name, value) {
1043
          if( $.isPlainObject(name) ) {
1044
            $.extend(true, module, name);
1045
          }
1046
          else if(value !== undefined) {
1047
            module[name] = value;
1048
          }
1049
          else {
1050
            return module[name];
1051
          }
1052
        },
1053
        debug: function() {
1054
          if(!settings.silent && settings.debug) {
1055
            if(settings.performance) {
1056
              module.performance.log(arguments);
1057
            }
1058
            else {
1059
              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1060
              module.debug.apply(console, arguments);
1061
            }
1062
          }
1063
        },
1064
        verbose: function() {
1065
          if(!settings.silent && settings.verbose && settings.debug) {
1066
            if(settings.performance) {
1067
              module.performance.log(arguments);
1068
            }
1069
            else {
1070
              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1071
              module.verbose.apply(console, arguments);
1072
            }
1073
          }
1074
        },
1075
        error: function() {
1076
          if(!settings.silent) {
1077
            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1078
            module.error.apply(console, arguments);
1079
          }
1080
        },
1081
        performance: {
1082
          log: function(message) {
1083
            var
1084
              currentTime,
1085
              executionTime,
1086
              previousTime
1087
            ;
1088
            if(settings.performance) {
1089
              currentTime   = new Date().getTime();
1090
              previousTime  = time || currentTime;
1091
              executionTime = currentTime - previousTime;
1092
              time          = currentTime;
1093
              performance.push({
1094
                'Name'           : message[0],
1095
                'Arguments'      : [].slice.call(message, 1) || '',
1096
                'Element'        : element,
1097
                'Execution Time' : executionTime
1098
              });
1099
            }
1100
            clearTimeout(module.performance.timer);
1101
            module.performance.timer = setTimeout(module.performance.display, 500);
1102
          },
1103
          display: function() {
1104
            var
1105
              title = settings.name + ':',
1106
              totalTime = 0
1107
            ;
1108
            time = false;
1109
            clearTimeout(module.performance.timer);
1110
            $.each(performance, function(index, data) {
1111
              totalTime += data['Execution Time'];
1112
            });
1113
            title += ' ' + totalTime + 'ms';
1114
            if(moduleSelector) {
1115
              title += ' \'' + moduleSelector + '\'';
1116
            }
1117
            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
1118
              console.groupCollapsed(title);
1119
              if(console.table) {
1120
                console.table(performance);
1121
              }
1122
              else {
1123
                $.each(performance, function(index, data) {
1124
                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
1125
                });
1126
              }
1127
              console.groupEnd();
1128
            }
1129
            performance = [];
1130
          }
1131
        },
1132
        invoke: function(query, passedArguments, context) {
1133
          var
1134
            object = instance,
1135
            maxDepth,
1136
            found,
1137
            response
1138
          ;
1139
          passedArguments = passedArguments || queryArguments;
1140
          context         = element         || context;
1141
          if(typeof query == 'string' && object !== undefined) {
1142
            query    = query.split(/[\. ]/);
1143
            maxDepth = query.length - 1;
1144
            $.each(query, function(depth, value) {
1145
              var camelCaseValue = (depth != maxDepth)
1146
                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1147
                : query
1148
              ;
1149
              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
1150
                object = object[camelCaseValue];
1151
              }
1152
              else if( object[camelCaseValue] !== undefined ) {
1153
                found = object[camelCaseValue];
1154
                return false;
1155
              }
1156
              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
1157
                object = object[value];
1158
              }
1159
              else if( object[value] !== undefined ) {
1160
                found = object[value];
1161
                return false;
1162
              }
1163
              else {
1164
                module.error(error.method, query);
1165
                return false;
1166
              }
1167
            });
1168
          }
1169
          if ( $.isFunction( found ) ) {
1170
            response = found.apply(context, passedArguments);
1171
          }
1172
          else if(found !== undefined) {
1173
            response = found;
1174
          }
1175
          if($.isArray(returnedValue)) {
1176
            returnedValue.push(response);
1177
          }
1178
          else if(returnedValue !== undefined) {
1179
            returnedValue = [returnedValue, response];
1180
          }
1181
          else if(response !== undefined) {
1182
            returnedValue = response;
1183
          }
1184
          return found;
1185
        }
1186
      };
1187
1188
      if(methodInvoked) {
1189
        if(instance === undefined) {
1190
          module.initialize();
1191
        }
1192
        instance.save.scroll();
1193
        instance.save.calculations();
1194
        module.invoke(query);
1195
      }
1196
      else {
1197
        if(instance !== undefined) {
1198
          instance.invoke('destroy');
1199
        }
1200
        module.initialize();
1201
      }
1202
    })
1203
  ;
1204
1205
  return (returnedValue !== undefined)
1206
    ? returnedValue
1207
    : this
1208
  ;
1209
};
1210
1211
$.fn.visibility.settings = {
1212
1213
  name                   : 'Visibility',
1214
  namespace              : 'visibility',
1215
1216
  debug                  : false,
1217
  verbose                : false,
1218
  performance            : true,
1219
1220
  // whether to use mutation observers to follow changes
1221
  observeChanges         : true,
1222
1223
  // check position immediately on init
1224
  initialCheck           : true,
1225
1226
  // whether to refresh calculations after all page images load
1227
  refreshOnLoad          : true,
1228
1229
  // whether to refresh calculations after page resize event
1230
  refreshOnResize        : true,
1231
1232
  // should call callbacks on refresh event (resize, etc)
1233
  checkOnRefresh         : true,
1234
1235
  // callback should only occur one time
1236
  once                   : true,
1237
1238
  // callback should fire continuously whe evaluates to true
1239
  continuous             : false,
1240
1241
  // offset to use with scroll top
1242
  offset                 : 0,
1243
1244
  // whether to include margin in elements position
1245
  includeMargin          : false,
1246
1247
  // scroll context for visibility checks
1248
  context                : window,
1249
1250
  // visibility check delay in ms (defaults to animationFrame)
1251
  throttle               : false,
1252
1253
  // special visibility type (image, fixed)
1254
  type                   : false,
1255
1256
  // z-index to use with visibility 'fixed'
1257
  zIndex                 : '10',
1258
1259
  // image only animation settings
1260
  transition             : 'fade in',
1261
  duration               : 1000,
1262
1263
  // array of callbacks for percentage
1264
  onPassed               : {},
1265
1266
  // standard callbacks
1267
  onOnScreen             : false,
1268
  onOffScreen            : false,
1269
  onPassing              : false,
1270
  onTopVisible           : false,
1271
  onBottomVisible        : false,
1272
  onTopPassed            : false,
1273
  onBottomPassed         : false,
1274
1275
  // reverse callbacks
1276
  onPassingReverse       : false,
1277
  onTopVisibleReverse    : false,
1278
  onBottomVisibleReverse : false,
1279
  onTopPassedReverse     : false,
1280
  onBottomPassedReverse  : false,
1281
1282
  // special callbacks for image
1283
  onLoad                 : function() {},
1284
  onAllLoaded            : function() {},
1285
1286
  // special callbacks for fixed position
1287
  onFixed                : function() {},
1288
  onUnfixed              : function() {},
1289
1290
  // utility callbacks
1291
  onUpdate               : false, // disabled by default for performance
1292
  onRefresh              : function(){},
1293
1294
  metadata : {
1295
    src: 'src'
1296
  },
1297
1298
  className: {
1299
    fixed       : 'fixed',
1300
    placeholder : 'placeholder',
1301
    visible     : 'visible'
1302
  },
1303
1304
  error : {
1305
    method  : 'The method you called is not defined.',
1306
    visible : 'Element is hidden, you must call refresh after element becomes visible'
1307
  }
1308
1309
};
1310
1311
})( jQuery, window, document );
1312