balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

waypoints.js (17964B)


      1 /*!
      2 Waypoints - 4.0.1
      3 Copyright © 2011-2016 Caleb Troughton
      4 Licensed under the MIT license.
      5 https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
      6 */
      7 (function() {
      8   'use strict'
      9 
     10   var keyCounter = 0
     11   var allWaypoints = {}
     12 
     13   /* http://imakewebthings.com/waypoints/api/waypoint */
     14   function Waypoint(options) {
     15     if (!options) {
     16       throw new Error('No options passed to Waypoint constructor')
     17     }
     18     if (!options.element) {
     19       throw new Error('No element option passed to Waypoint constructor')
     20     }
     21     if (!options.handler) {
     22       throw new Error('No handler option passed to Waypoint constructor')
     23     }
     24 
     25     this.key = 'waypoint-' + keyCounter
     26     this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
     27     this.element = this.options.element
     28     this.adapter = new Waypoint.Adapter(this.element)
     29     this.callback = options.handler
     30     this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
     31     this.enabled = this.options.enabled
     32     this.triggerPoint = null
     33     this.group = Waypoint.Group.findOrCreate({
     34       name: this.options.group,
     35       axis: this.axis
     36     })
     37     this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
     38 
     39     if (Waypoint.offsetAliases[this.options.offset]) {
     40       this.options.offset = Waypoint.offsetAliases[this.options.offset]
     41     }
     42     this.group.add(this)
     43     this.context.add(this)
     44     allWaypoints[this.key] = this
     45     keyCounter += 1
     46   }
     47 
     48   /* Private */
     49   Waypoint.prototype.queueTrigger = function(direction) {
     50     this.group.queueTrigger(this, direction)
     51   }
     52 
     53   /* Private */
     54   Waypoint.prototype.trigger = function(args) {
     55     if (!this.enabled) {
     56       return
     57     }
     58     if (this.callback) {
     59       this.callback.apply(this, args)
     60     }
     61   }
     62 
     63   /* Public */
     64   /* http://imakewebthings.com/waypoints/api/destroy */
     65   Waypoint.prototype.destroy = function() {
     66     this.context.remove(this)
     67     this.group.remove(this)
     68     delete allWaypoints[this.key]
     69   }
     70 
     71   /* Public */
     72   /* http://imakewebthings.com/waypoints/api/disable */
     73   Waypoint.prototype.disable = function() {
     74     this.enabled = false
     75     return this
     76   }
     77 
     78   /* Public */
     79   /* http://imakewebthings.com/waypoints/api/enable */
     80   Waypoint.prototype.enable = function() {
     81     this.context.refresh()
     82     this.enabled = true
     83     return this
     84   }
     85 
     86   /* Public */
     87   /* http://imakewebthings.com/waypoints/api/next */
     88   Waypoint.prototype.next = function() {
     89     return this.group.next(this)
     90   }
     91 
     92   /* Public */
     93   /* http://imakewebthings.com/waypoints/api/previous */
     94   Waypoint.prototype.previous = function() {
     95     return this.group.previous(this)
     96   }
     97 
     98   /* Private */
     99   Waypoint.invokeAll = function(method) {
    100     var allWaypointsArray = []
    101     for (var waypointKey in allWaypoints) {
    102       allWaypointsArray.push(allWaypoints[waypointKey])
    103     }
    104     for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
    105       allWaypointsArray[i][method]()
    106     }
    107   }
    108 
    109   /* Public */
    110   /* http://imakewebthings.com/waypoints/api/destroy-all */
    111   Waypoint.destroyAll = function() {
    112     Waypoint.invokeAll('destroy')
    113   }
    114 
    115   /* Public */
    116   /* http://imakewebthings.com/waypoints/api/disable-all */
    117   Waypoint.disableAll = function() {
    118     Waypoint.invokeAll('disable')
    119   }
    120 
    121   /* Public */
    122   /* http://imakewebthings.com/waypoints/api/enable-all */
    123   Waypoint.enableAll = function() {
    124     Waypoint.Context.refreshAll()
    125     for (var waypointKey in allWaypoints) {
    126       allWaypoints[waypointKey].enabled = true
    127     }
    128     return this
    129   }
    130 
    131   /* Public */
    132   /* http://imakewebthings.com/waypoints/api/refresh-all */
    133   Waypoint.refreshAll = function() {
    134     Waypoint.Context.refreshAll()
    135   }
    136 
    137   /* Public */
    138   /* http://imakewebthings.com/waypoints/api/viewport-height */
    139   Waypoint.viewportHeight = function() {
    140     return window.innerHeight || document.documentElement.clientHeight
    141   }
    142 
    143   /* Public */
    144   /* http://imakewebthings.com/waypoints/api/viewport-width */
    145   Waypoint.viewportWidth = function() {
    146     return document.documentElement.clientWidth
    147   }
    148 
    149   Waypoint.adapters = []
    150 
    151   Waypoint.defaults = {
    152     context: window,
    153     continuous: true,
    154     enabled: true,
    155     group: 'default',
    156     horizontal: false,
    157     offset: 0
    158   }
    159 
    160   Waypoint.offsetAliases = {
    161     'bottom-in-view': function() {
    162       return this.context.innerHeight() - this.adapter.outerHeight()
    163     },
    164     'right-in-view': function() {
    165       return this.context.innerWidth() - this.adapter.outerWidth()
    166     }
    167   }
    168 
    169   window.Waypoint = Waypoint
    170 }())
    171 ;(function() {
    172   'use strict'
    173 
    174   function requestAnimationFrameShim(callback) {
    175     window.setTimeout(callback, 1000 / 60)
    176   }
    177 
    178   var keyCounter = 0
    179   var contexts = {}
    180   var Waypoint = window.Waypoint
    181   var oldWindowLoad = window.onload
    182 
    183   /* http://imakewebthings.com/waypoints/api/context */
    184   function Context(element) {
    185     this.element = element
    186     this.Adapter = Waypoint.Adapter
    187     this.adapter = new this.Adapter(element)
    188     this.key = 'waypoint-context-' + keyCounter
    189     this.didScroll = false
    190     this.didResize = false
    191     this.oldScroll = {
    192       x: this.adapter.scrollLeft(),
    193       y: this.adapter.scrollTop()
    194     }
    195     this.waypoints = {
    196       vertical: {},
    197       horizontal: {}
    198     }
    199 
    200     element.waypointContextKey = this.key
    201     contexts[element.waypointContextKey] = this
    202     keyCounter += 1
    203     if (!Waypoint.windowContext) {
    204       Waypoint.windowContext = true
    205       Waypoint.windowContext = new Context(window)
    206     }
    207 
    208     this.createThrottledScrollHandler()
    209     this.createThrottledResizeHandler()
    210   }
    211 
    212   /* Private */
    213   Context.prototype.add = function(waypoint) {
    214     var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
    215     this.waypoints[axis][waypoint.key] = waypoint
    216     this.refresh()
    217   }
    218 
    219   /* Private */
    220   Context.prototype.checkEmpty = function() {
    221     var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
    222     var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
    223     var isWindow = this.element == this.element.window
    224     if (horizontalEmpty && verticalEmpty && !isWindow) {
    225       this.adapter.off('.waypoints')
    226       delete contexts[this.key]
    227     }
    228   }
    229 
    230   /* Private */
    231   Context.prototype.createThrottledResizeHandler = function() {
    232     var self = this
    233 
    234     function resizeHandler() {
    235       self.handleResize()
    236       self.didResize = false
    237     }
    238 
    239     this.adapter.on('resize.waypoints', function() {
    240       if (!self.didResize) {
    241         self.didResize = true
    242         Waypoint.requestAnimationFrame(resizeHandler)
    243       }
    244     })
    245   }
    246 
    247   /* Private */
    248   Context.prototype.createThrottledScrollHandler = function() {
    249     var self = this
    250     function scrollHandler() {
    251       self.handleScroll()
    252       self.didScroll = false
    253     }
    254 
    255     this.adapter.on('scroll.waypoints', function() {
    256       if (!self.didScroll || Waypoint.isTouch) {
    257         self.didScroll = true
    258         Waypoint.requestAnimationFrame(scrollHandler)
    259       }
    260     })
    261   }
    262 
    263   /* Private */
    264   Context.prototype.handleResize = function() {
    265     Waypoint.Context.refreshAll()
    266   }
    267 
    268   /* Private */
    269   Context.prototype.handleScroll = function() {
    270     var triggeredGroups = {}
    271     var axes = {
    272       horizontal: {
    273         newScroll: this.adapter.scrollLeft(),
    274         oldScroll: this.oldScroll.x,
    275         forward: 'right',
    276         backward: 'left'
    277       },
    278       vertical: {
    279         newScroll: this.adapter.scrollTop(),
    280         oldScroll: this.oldScroll.y,
    281         forward: 'down',
    282         backward: 'up'
    283       }
    284     }
    285 
    286     for (var axisKey in axes) {
    287       var axis = axes[axisKey]
    288       var isForward = axis.newScroll > axis.oldScroll
    289       var direction = isForward ? axis.forward : axis.backward
    290 
    291       for (var waypointKey in this.waypoints[axisKey]) {
    292         var waypoint = this.waypoints[axisKey][waypointKey]
    293         if (waypoint.triggerPoint === null) {
    294           continue
    295         }
    296         var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
    297         var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
    298         var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
    299         var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
    300         if (crossedForward || crossedBackward) {
    301           waypoint.queueTrigger(direction)
    302           triggeredGroups[waypoint.group.id] = waypoint.group
    303         }
    304       }
    305     }
    306 
    307     for (var groupKey in triggeredGroups) {
    308       triggeredGroups[groupKey].flushTriggers()
    309     }
    310 
    311     this.oldScroll = {
    312       x: axes.horizontal.newScroll,
    313       y: axes.vertical.newScroll
    314     }
    315   }
    316 
    317   /* Private */
    318   Context.prototype.innerHeight = function() {
    319     /*eslint-disable eqeqeq */
    320     if (this.element == this.element.window) {
    321       return Waypoint.viewportHeight()
    322     }
    323     /*eslint-enable eqeqeq */
    324     return this.adapter.innerHeight()
    325   }
    326 
    327   /* Private */
    328   Context.prototype.remove = function(waypoint) {
    329     delete this.waypoints[waypoint.axis][waypoint.key]
    330     this.checkEmpty()
    331   }
    332 
    333   /* Private */
    334   Context.prototype.innerWidth = function() {
    335     /*eslint-disable eqeqeq */
    336     if (this.element == this.element.window) {
    337       return Waypoint.viewportWidth()
    338     }
    339     /*eslint-enable eqeqeq */
    340     return this.adapter.innerWidth()
    341   }
    342 
    343   /* Public */
    344   /* http://imakewebthings.com/waypoints/api/context-destroy */
    345   Context.prototype.destroy = function() {
    346     var allWaypoints = []
    347     for (var axis in this.waypoints) {
    348       for (var waypointKey in this.waypoints[axis]) {
    349         allWaypoints.push(this.waypoints[axis][waypointKey])
    350       }
    351     }
    352     for (var i = 0, end = allWaypoints.length; i < end; i++) {
    353       allWaypoints[i].destroy()
    354     }
    355   }
    356 
    357   /* Public */
    358   /* http://imakewebthings.com/waypoints/api/context-refresh */
    359   Context.prototype.refresh = function() {
    360     /*eslint-disable eqeqeq */
    361     var isWindow = this.element == this.element.window
    362     /*eslint-enable eqeqeq */
    363     var contextOffset = isWindow ? undefined : this.adapter.offset()
    364     var triggeredGroups = {}
    365     var axes
    366 
    367     this.handleScroll()
    368     axes = {
    369       horizontal: {
    370         contextOffset: isWindow ? 0 : contextOffset.left,
    371         contextScroll: isWindow ? 0 : this.oldScroll.x,
    372         contextDimension: this.innerWidth(),
    373         oldScroll: this.oldScroll.x,
    374         forward: 'right',
    375         backward: 'left',
    376         offsetProp: 'left'
    377       },
    378       vertical: {
    379         contextOffset: isWindow ? 0 : contextOffset.top,
    380         contextScroll: isWindow ? 0 : this.oldScroll.y,
    381         contextDimension: this.innerHeight(),
    382         oldScroll: this.oldScroll.y,
    383         forward: 'down',
    384         backward: 'up',
    385         offsetProp: 'top'
    386       }
    387     }
    388 
    389     for (var axisKey in axes) {
    390       var axis = axes[axisKey]
    391       for (var waypointKey in this.waypoints[axisKey]) {
    392         var waypoint = this.waypoints[axisKey][waypointKey]
    393         var adjustment = waypoint.options.offset
    394         var oldTriggerPoint = waypoint.triggerPoint
    395         var elementOffset = 0
    396         var freshWaypoint = oldTriggerPoint == null
    397         var contextModifier, wasBeforeScroll, nowAfterScroll
    398         var triggeredBackward, triggeredForward
    399 
    400         if (waypoint.element !== waypoint.element.window) {
    401           elementOffset = waypoint.adapter.offset()[axis.offsetProp]
    402         }
    403 
    404         if (typeof adjustment === 'function') {
    405           adjustment = adjustment.apply(waypoint)
    406         }
    407         else if (typeof adjustment === 'string') {
    408           adjustment = parseFloat(adjustment)
    409           if (waypoint.options.offset.indexOf('%') > - 1) {
    410             adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
    411           }
    412         }
    413 
    414         contextModifier = axis.contextScroll - axis.contextOffset
    415         waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
    416         wasBeforeScroll = oldTriggerPoint < axis.oldScroll
    417         nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
    418         triggeredBackward = wasBeforeScroll && nowAfterScroll
    419         triggeredForward = !wasBeforeScroll && !nowAfterScroll
    420 
    421         if (!freshWaypoint && triggeredBackward) {
    422           waypoint.queueTrigger(axis.backward)
    423           triggeredGroups[waypoint.group.id] = waypoint.group
    424         }
    425         else if (!freshWaypoint && triggeredForward) {
    426           waypoint.queueTrigger(axis.forward)
    427           triggeredGroups[waypoint.group.id] = waypoint.group
    428         }
    429         else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
    430           waypoint.queueTrigger(axis.forward)
    431           triggeredGroups[waypoint.group.id] = waypoint.group
    432         }
    433       }
    434     }
    435 
    436     Waypoint.requestAnimationFrame(function() {
    437       for (var groupKey in triggeredGroups) {
    438         triggeredGroups[groupKey].flushTriggers()
    439       }
    440     })
    441 
    442     return this
    443   }
    444 
    445   /* Private */
    446   Context.findOrCreateByElement = function(element) {
    447     return Context.findByElement(element) || new Context(element)
    448   }
    449 
    450   /* Private */
    451   Context.refreshAll = function() {
    452     for (var contextId in contexts) {
    453       contexts[contextId].refresh()
    454     }
    455   }
    456 
    457   /* Public */
    458   /* http://imakewebthings.com/waypoints/api/context-find-by-element */
    459   Context.findByElement = function(element) {
    460     return contexts[element.waypointContextKey]
    461   }
    462 
    463   window.onload = function() {
    464     if (oldWindowLoad) {
    465       oldWindowLoad()
    466     }
    467     Context.refreshAll()
    468   }
    469 
    470 
    471   Waypoint.requestAnimationFrame = function(callback) {
    472     var requestFn = window.requestAnimationFrame ||
    473       window.mozRequestAnimationFrame ||
    474       window.webkitRequestAnimationFrame ||
    475       requestAnimationFrameShim
    476     requestFn.call(window, callback)
    477   }
    478   Waypoint.Context = Context
    479 }())
    480 ;(function() {
    481   'use strict'
    482 
    483   function byTriggerPoint(a, b) {
    484     return a.triggerPoint - b.triggerPoint
    485   }
    486 
    487   function byReverseTriggerPoint(a, b) {
    488     return b.triggerPoint - a.triggerPoint
    489   }
    490 
    491   var groups = {
    492     vertical: {},
    493     horizontal: {}
    494   }
    495   var Waypoint = window.Waypoint
    496 
    497   /* http://imakewebthings.com/waypoints/api/group */
    498   function Group(options) {
    499     this.name = options.name
    500     this.axis = options.axis
    501     this.id = this.name + '-' + this.axis
    502     this.waypoints = []
    503     this.clearTriggerQueues()
    504     groups[this.axis][this.name] = this
    505   }
    506 
    507   /* Private */
    508   Group.prototype.add = function(waypoint) {
    509     this.waypoints.push(waypoint)
    510   }
    511 
    512   /* Private */
    513   Group.prototype.clearTriggerQueues = function() {
    514     this.triggerQueues = {
    515       up: [],
    516       down: [],
    517       left: [],
    518       right: []
    519     }
    520   }
    521 
    522   /* Private */
    523   Group.prototype.flushTriggers = function() {
    524     for (var direction in this.triggerQueues) {
    525       var waypoints = this.triggerQueues[direction]
    526       var reverse = direction === 'up' || direction === 'left'
    527       waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
    528       for (var i = 0, end = waypoints.length; i < end; i += 1) {
    529         var waypoint = waypoints[i]
    530         if (waypoint.options.continuous || i === waypoints.length - 1) {
    531           waypoint.trigger([direction])
    532         }
    533       }
    534     }
    535     this.clearTriggerQueues()
    536   }
    537 
    538   /* Private */
    539   Group.prototype.next = function(waypoint) {
    540     this.waypoints.sort(byTriggerPoint)
    541     var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
    542     var isLast = index === this.waypoints.length - 1
    543     return isLast ? null : this.waypoints[index + 1]
    544   }
    545 
    546   /* Private */
    547   Group.prototype.previous = function(waypoint) {
    548     this.waypoints.sort(byTriggerPoint)
    549     var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
    550     return index ? this.waypoints[index - 1] : null
    551   }
    552 
    553   /* Private */
    554   Group.prototype.queueTrigger = function(waypoint, direction) {
    555     this.triggerQueues[direction].push(waypoint)
    556   }
    557 
    558   /* Private */
    559   Group.prototype.remove = function(waypoint) {
    560     var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
    561     if (index > -1) {
    562       this.waypoints.splice(index, 1)
    563     }
    564   }
    565 
    566   /* Public */
    567   /* http://imakewebthings.com/waypoints/api/first */
    568   Group.prototype.first = function() {
    569     return this.waypoints[0]
    570   }
    571 
    572   /* Public */
    573   /* http://imakewebthings.com/waypoints/api/last */
    574   Group.prototype.last = function() {
    575     return this.waypoints[this.waypoints.length - 1]
    576   }
    577 
    578   /* Private */
    579   Group.findOrCreate = function(options) {
    580     return groups[options.axis][options.name] || new Group(options)
    581   }
    582 
    583   Waypoint.Group = Group
    584 }())
    585 ;(function() {
    586   'use strict'
    587 
    588   var $ = window.jQuery
    589   var Waypoint = window.Waypoint
    590 
    591   function JQueryAdapter(element) {
    592     this.$element = $(element)
    593   }
    594 
    595   $.each([
    596     'innerHeight',
    597     'innerWidth',
    598     'off',
    599     'offset',
    600     'on',
    601     'outerHeight',
    602     'outerWidth',
    603     'scrollLeft',
    604     'scrollTop'
    605   ], function(i, method) {
    606     JQueryAdapter.prototype[method] = function() {
    607       var args = Array.prototype.slice.call(arguments)
    608       return this.$element[method].apply(this.$element, args)
    609     }
    610   })
    611 
    612   $.each([
    613     'extend',
    614     'inArray',
    615     'isEmptyObject'
    616   ], function(i, method) {
    617     JQueryAdapter[method] = $[method]
    618   })
    619 
    620   Waypoint.adapters.push({
    621     name: 'jquery',
    622     Adapter: JQueryAdapter
    623   })
    624   Waypoint.Adapter = JQueryAdapter
    625 }())
    626 ;(function() {
    627   'use strict'
    628 
    629   var Waypoint = window.Waypoint
    630 
    631   function createExtension(framework) {
    632     return function() {
    633       var waypoints = []
    634       var overrides = arguments[0]
    635 
    636       if (framework.isFunction(arguments[0])) {
    637         overrides = framework.extend({}, arguments[1])
    638         overrides.handler = arguments[0]
    639       }
    640 
    641       this.each(function() {
    642         var options = framework.extend({}, overrides, {
    643           element: this
    644         })
    645         if (typeof options.context === 'string') {
    646           options.context = framework(this).closest(options.context)[0]
    647         }
    648         waypoints.push(new Waypoint(options))
    649       })
    650 
    651       return waypoints
    652     }
    653   }
    654 
    655   if (window.jQuery) {
    656     window.jQuery.fn.elementorWaypoint = createExtension(window.jQuery)
    657   }
    658   if (window.Zepto) {
    659     window.Zepto.fn.elementorWaypoint = createExtension(window.Zepto)
    660   }
    661 }())
    662 ;