github.com/mattyr/nomad@v0.3.3-0.20160919021406-3485a065154a/website/source/assets/javascripts/lib/_jquery.waypoints.js (about)

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