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 ;