| //Licensed to the Apache Software Foundation (ASF) under one or more |
| //contributor license agreements. See the NOTICE file distributed with |
| //this work for additional information regarding copyright ownership. |
| //The ASF licenses this file to You under the Apache License, Version 2.0 |
| //(the "License"); you may not use this file except in compliance with |
| //the License. You may obtain a copy of the License at |
| // |
| //http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| //Unless required by applicable law or agreed to in writing, software |
| //distributed under the License is distributed on an "AS IS" BASIS, |
| //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| //express or implied. See the License for the specific language governing |
| //permissions and limitations under the License. |
| |
| (function() { |
| 'use strict' |
| |
| function requestAnimationFrameShim(callback) { |
| window.setTimeout(callback, 1000 / 60) |
| } |
| |
| var keyCounter = 0 |
| var contexts = {} |
| var Waypoint = window.Waypoint |
| var oldWindowLoad = window.onload |
| |
| /* http://imakewebthings.com/waypoints/api/context */ |
| function Context(element) { |
| this.element = element |
| this.Adapter = Waypoint.Adapter |
| this.adapter = new this.Adapter(element) |
| this.key = 'waypoint-context-' + keyCounter |
| this.didScroll = false |
| this.didResize = false |
| this.oldScroll = { |
| x: this.adapter.scrollLeft(), |
| y: this.adapter.scrollTop() |
| } |
| this.waypoints = { |
| vertical: {}, |
| horizontal: {} |
| } |
| |
| element.waypointContextKey = this.key |
| contexts[element.waypointContextKey] = this |
| keyCounter += 1 |
| |
| this.createThrottledScrollHandler() |
| this.createThrottledResizeHandler() |
| } |
| |
| /* Private */ |
| Context.prototype.add = function(waypoint) { |
| var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical' |
| this.waypoints[axis][waypoint.key] = waypoint |
| this.refresh() |
| } |
| |
| /* Private */ |
| Context.prototype.checkEmpty = function() { |
| var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal) |
| var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical) |
| if (horizontalEmpty && verticalEmpty) { |
| this.adapter.off('.waypoints') |
| delete contexts[this.key] |
| } |
| } |
| |
| /* Private */ |
| Context.prototype.createThrottledResizeHandler = function() { |
| var self = this |
| |
| function resizeHandler() { |
| self.handleResize() |
| self.didResize = false |
| } |
| |
| this.adapter.on('resize.waypoints', function() { |
| if (!self.didResize) { |
| self.didResize = true |
| Waypoint.requestAnimationFrame(resizeHandler) |
| } |
| }) |
| } |
| |
| /* Private */ |
| Context.prototype.createThrottledScrollHandler = function() { |
| var self = this |
| function scrollHandler() { |
| self.handleScroll() |
| self.didScroll = false |
| } |
| |
| this.adapter.on('scroll.waypoints', function() { |
| if (!self.didScroll || Waypoint.isTouch) { |
| self.didScroll = true |
| Waypoint.requestAnimationFrame(scrollHandler) |
| } |
| }) |
| } |
| |
| /* Private */ |
| Context.prototype.handleResize = function() { |
| Waypoint.Context.refreshAll() |
| } |
| |
| /* Private */ |
| Context.prototype.handleScroll = function() { |
| var triggeredGroups = {} |
| var axes = { |
| horizontal: { |
| newScroll: this.adapter.scrollLeft(), |
| oldScroll: this.oldScroll.x, |
| forward: 'right', |
| backward: 'left' |
| }, |
| vertical: { |
| newScroll: this.adapter.scrollTop(), |
| oldScroll: this.oldScroll.y, |
| forward: 'down', |
| backward: 'up' |
| } |
| } |
| |
| for (var axisKey in axes) { |
| var axis = axes[axisKey] |
| var isForward = axis.newScroll > axis.oldScroll |
| var direction = isForward ? axis.forward : axis.backward |
| |
| for (var waypointKey in this.waypoints[axisKey]) { |
| var waypoint = this.waypoints[axisKey][waypointKey] |
| var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint |
| var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint |
| var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint |
| var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint |
| if (crossedForward || crossedBackward) { |
| waypoint.queueTrigger(direction) |
| triggeredGroups[waypoint.group.id] = waypoint.group |
| } |
| } |
| } |
| |
| for (var groupKey in triggeredGroups) { |
| triggeredGroups[groupKey].flushTriggers() |
| } |
| |
| this.oldScroll = { |
| x: axes.horizontal.newScroll, |
| y: axes.vertical.newScroll |
| } |
| } |
| |
| /* Private */ |
| Context.prototype.innerHeight = function() { |
| /*eslint-disable eqeqeq */ |
| if (this.element == this.element.window) { |
| return Waypoint.viewportHeight() |
| } |
| /*eslint-enable eqeqeq */ |
| return this.adapter.innerHeight() |
| } |
| |
| /* Private */ |
| Context.prototype.remove = function(waypoint) { |
| delete this.waypoints[waypoint.axis][waypoint.key] |
| this.checkEmpty() |
| } |
| |
| /* Private */ |
| Context.prototype.innerWidth = function() { |
| /*eslint-disable eqeqeq */ |
| if (this.element == this.element.window) { |
| return Waypoint.viewportWidth() |
| } |
| /*eslint-enable eqeqeq */ |
| return this.adapter.innerWidth() |
| } |
| |
| /* Public */ |
| /* http://imakewebthings.com/waypoints/api/context-destroy */ |
| Context.prototype.destroy = function() { |
| var allWaypoints = [] |
| for (var axis in this.waypoints) { |
| for (var waypointKey in this.waypoints[axis]) { |
| allWaypoints.push(this.waypoints[axis][waypointKey]) |
| } |
| } |
| for (var i = 0, end = allWaypoints.length; i < end; i++) { |
| allWaypoints[i].destroy() |
| } |
| } |
| |
| /* Public */ |
| /* http://imakewebthings.com/waypoints/api/context-refresh */ |
| Context.prototype.refresh = function() { |
| /*eslint-disable eqeqeq */ |
| var isWindow = this.element == this.element.window |
| /*eslint-enable eqeqeq */ |
| var contextOffset = isWindow ? undefined : this.adapter.offset() |
| var triggeredGroups = {} |
| var axes |
| |
| this.handleScroll() |
| axes = { |
| horizontal: { |
| contextOffset: isWindow ? 0 : contextOffset.left, |
| contextScroll: isWindow ? 0 : this.oldScroll.x, |
| contextDimension: this.innerWidth(), |
| oldScroll: this.oldScroll.x, |
| forward: 'right', |
| backward: 'left', |
| offsetProp: 'left' |
| }, |
| vertical: { |
| contextOffset: isWindow ? 0 : contextOffset.top, |
| contextScroll: isWindow ? 0 : this.oldScroll.y, |
| contextDimension: this.innerHeight(), |
| oldScroll: this.oldScroll.y, |
| forward: 'down', |
| backward: 'up', |
| offsetProp: 'top' |
| } |
| } |
| |
| for (var axisKey in axes) { |
| var axis = axes[axisKey] |
| for (var waypointKey in this.waypoints[axisKey]) { |
| var waypoint = this.waypoints[axisKey][waypointKey] |
| var adjustment = waypoint.options.offset |
| var oldTriggerPoint = waypoint.triggerPoint |
| var elementOffset = 0 |
| var freshWaypoint = oldTriggerPoint == null |
| var contextModifier, wasBeforeScroll, nowAfterScroll |
| var triggeredBackward, triggeredForward |
| |
| if (waypoint.element !== waypoint.element.window) { |
| elementOffset = waypoint.adapter.offset()[axis.offsetProp] |
| } |
| |
| if (typeof adjustment === 'function') { |
| adjustment = adjustment.apply(waypoint) |
| } |
| else if (typeof adjustment === 'string') { |
| adjustment = parseFloat(adjustment) |
| if (waypoint.options.offset.indexOf('%') > - 1) { |
| adjustment = Math.ceil(axis.contextDimension * adjustment / 100) |
| } |
| } |
| |
| contextModifier = axis.contextScroll - axis.contextOffset |
| waypoint.triggerPoint = elementOffset + contextModifier - adjustment |
| wasBeforeScroll = oldTriggerPoint < axis.oldScroll |
| nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll |
| triggeredBackward = wasBeforeScroll && nowAfterScroll |
| triggeredForward = !wasBeforeScroll && !nowAfterScroll |
| |
| if (!freshWaypoint && triggeredBackward) { |
| waypoint.queueTrigger(axis.backward) |
| triggeredGroups[waypoint.group.id] = waypoint.group |
| } |
| else if (!freshWaypoint && triggeredForward) { |
| waypoint.queueTrigger(axis.forward) |
| triggeredGroups[waypoint.group.id] = waypoint.group |
| } |
| else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) { |
| waypoint.queueTrigger(axis.forward) |
| triggeredGroups[waypoint.group.id] = waypoint.group |
| } |
| } |
| } |
| |
| Waypoint.requestAnimationFrame(function() { |
| for (var groupKey in triggeredGroups) { |
| triggeredGroups[groupKey].flushTriggers() |
| } |
| }) |
| |
| return this |
| } |
| |
| /* Private */ |
| Context.findOrCreateByElement = function(element) { |
| return Context.findByElement(element) || new Context(element) |
| } |
| |
| /* Private */ |
| Context.refreshAll = function() { |
| for (var contextId in contexts) { |
| contexts[contextId].refresh() |
| } |
| } |
| |
| /* Public */ |
| /* http://imakewebthings.com/waypoints/api/context-find-by-element */ |
| Context.findByElement = function(element) { |
| return contexts[element.waypointContextKey] |
| } |
| |
| window.onload = function() { |
| if (oldWindowLoad) { |
| oldWindowLoad() |
| } |
| Context.refreshAll() |
| } |
| |
| Waypoint.requestAnimationFrame = function(callback) { |
| var requestFn = window.requestAnimationFrame || |
| window.mozRequestAnimationFrame || |
| window.webkitRequestAnimationFrame || |
| requestAnimationFrameShim |
| requestFn.call(window, callback) |
| } |
| Waypoint.Context = Context |
| }()) |