PageSourceSearch

https://pco-study.com/bower_components/angular-scroll.js

js pco-study.com collected 2026-10-08 22:12:53 UTC 19,830 bytes, 636 lines download raw bytes

1/**
2  * x is a value between 0 and 1, indicating where in the animation you are.
3  */
4var duScrollDefaultEasing = function (x) {
5  'use strict';
6
7  if(x < 0.5) {
8    return Math.pow(x*2, 2)/2;
9  }
10  return 1-Math.pow((1-x)*2, 2)/2;
11};
12
13var duScroll = angular.module('duScroll', [
14  'duScroll.scrollspy',
15  'duScroll.smoothScroll',
16  'duScroll.scrollContainer',
17  'duScroll.spyContext',
18  'duScroll.scrollHelpers'
19])
20  //Default animation duration for smoothScroll directive
21  .value('duScrollDuration', 350)
22  //Scrollspy debounce interval, set to 0 to disable
23  .value('duScrollSpyWait', 100)
24  //Scrollspy forced refresh interval, use if your content changes or reflows without scrolling.
25  //0 to disable
26  .value('duScrollSpyRefreshInterval', 0)
27  //Wether or not multiple scrollspies can be active at once
28  .value('duScrollGreedy', false)
29  //Default offset for smoothScroll directive
30  .value('duScrollOffset', 0)
31  //Default easing function for scroll animation
32  .value('duScrollEasing', duScrollDefaultEasing)
33  //Which events on the container (such as body) should cancel scroll animations
34  .value('duScrollCancelOnEvents', 'scroll mousedown mousewheel touchmove keydown')
35  //Whether or not to activate the last scrollspy, when page/container bottom is reached
36  .value('duScrollBottomSpy', false)
37  //Active class name
38  .value('duScrollActiveClass', 'active');
39
40if (typeof module !== 'undefined' && module && module.exports) {
41  module.exports = duScroll;
42}
43
44
45angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation'])
46.run(["$window", "$q", "cancelAnimation", "requestAnimation", "duScrollEasing", "duScrollDuration", "duScrollOffset", "duScrollCancelOnEvents", function($window, $q, cancelAnimation, requestAnimation, duScrollEasing, duScrollDuration, duScrollOffset, duScrollCancelOnEvents) {
47  'use strict';
48
49  var proto = {};
50
51  var isDocument = function(el) {
52    return (typeof HTMLDocument !== 'undefined' && el instanceof HTMLDocument) || (el.nodeType && el.nodeType === el.DOCUMENT_NODE);
53  };
54
55  var isElement = function(el) {
56    return (typeof HTMLElement !== 'undefined' && el instanceof HTMLElement) || (el.nodeType && el.nodeType === el.ELEMENT_NODE);
57  };
58
59  var unwrap = function(el) {
60    return isElement(el) || isDocument(el) ? el : el[0];
61  };
62
63  proto.duScrollTo = function(left, top, duration, easing) {
64    var aliasFn;
65    if(angular.isElement(left)) {
66      aliasFn = this.duScrollToElement;
67    } else if(angular.isDefined(duration)) {
68      aliasFn = this.duScrollToAnimated;
69    }
70    if(aliasFn) {
71      return aliasFn.apply(this, arguments);
72    }
73    var el = unwrap(this);
74    if(isDocument(el)) {
75      return $window.scrollTo(left, top);
76    }
77    el.scrollLeft = left;
78    el.scrollTop = top;
79  };
80
81  var scrollAnimation, deferred;
82  proto.duScrollToAnimated = function(left, top, duration, easing) {
83    if(duration && !easing) {
84      easing = duScrollEasing;
85    }
86    var startLeft = this.duScrollLeft(),
87        startTop = this.duScrollTop(),
88        deltaLeft = Math.round(left - startLeft),
89        deltaTop = Math.round(top - startTop);
90
91    var startTime = null, progress = 0;
92    var el = this;
93
94    var cancelScrollAnimation = function($event) {
95      if (!$event || (progress && $event.which > 0)) {
96        if(duScrollCancelOnEvents) {
97          el.unbind(duScrollCancelOnEvents, cancelScrollAnimation);
98        }
99        cancelAnimation(scrollAnimation);
100        deferred.reject();
101        scrollAnimation = null;
102      }
103    };
104
105    if(scrollAnimation) {
106      cancelScrollAnimation();
107    }
108    deferred = $q.defer();
109
110    if(duration === 0 || (!deltaLeft && !deltaTop)) {
111      if(duration === 0) {
112        el.duScrollTo(left, top);
113      }
114      deferred.resolve();
115      return deferred.promise;
116    }
117
118    var animationStep = function(timestamp) {
119      if (startTime === null) {
120        startTime = timestamp;
121      }
122
123      progress = timestamp - startTime;
124      var percent = (progress >= duration ? 1 : easing(progress/duration));
125
126      el.scrollTo(
127        startLeft + Math.ceil(deltaLeft * percent),
128        startTop + Math.ceil(deltaTop * percent)
129      );
130      if(percent < 1) {
131        scrollAnimation = requestAnimation(animationStep);
132      } else {
133        if(duScrollCancelOnEvents) {
134          el.unbind(duScrollCancelOnEvents, cancelScrollAnimation);
135        }
136        scrollAnimation = null;
137        deferred.resolve();
138      }
139    };
140
141    //Fix random mobile safari bug when scrolling to top by hitting status bar
142    el.duScrollTo(startLeft, startTop);
143
144    if(duScrollCancelOnEvents) {
145      el.bind(duScrollCancelOnEvents, cancelScrollAnimation);
146    }
147
148    scrollAnimation = requestAnimation(animationStep);
149    return deferred.promise;
150  };
151
152  proto.duScrollToElement = function(target, offset, duration, easing) {
153    var el = unwrap(this);
154    if(!angular.isNumber(offset) || isNaN(offset)) {
155      offset = duScrollOffset;
156    }
157    var top = this.duScrollTop() + unwrap(target).getBoundingClientRect().top - offset;
158    if(isElement(el)) {
159      top -= el.getBoundingClientRect().top;
160    }
161    return this.duScrollTo(0, top, duration, easing);
162  };
163
164  proto.duScrollLeft = function(value, duration, easing) {
165    if(angular.isNumber(value)) {
166      return this.duScrollTo(value, this.duScrollTop(), duration, easing);
167    }
168    var el = unwrap(this);
169    if(isDocument(el)) {
170      return $window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft;
171    }
172    return el.scrollLeft;
173  };
174  proto.duScrollTop = function(value, duration, easing) {
175    if(angular.isNumber(value)) {
176      return this.duScrollTo(this.duScrollLeft(), value, duration, easing);
177    }
178    var el = unwrap(this);
179    if(isDocument(el)) {
180      return $window.scrollY || document.documentElement.scrollTop || document.body.scrollTop;
181    }
182    return el.scrollTop;
183  };
184
185  proto.duScrollToElementAnimated = function(target, offset, duration, easing) {
186    return this.duScrollToElement(target, offset, duration || duScrollDuration, easing);
187  };
188
189  proto.duScrollTopAnimated = function(top, duration, easing) {
190    return this.duScrollTop(top, duration || duScrollDuration, easing);
191  };
192
193  proto.duScrollLeftAnimated = function(left, duration, easing) {
194    return this.duScrollLeft(left, duration || duScrollDuration, easing);
195  };
196
197  angular.forEach(proto, function(fn, key) {
198    angular.element.prototype[key] = fn;
199
200    //Remove prefix if not already claimed by jQuery / ui.utils
201    var unprefixed = key.replace(/^duScroll/, 'scroll');
202    if(angular.isUndefined(angular.element.prototype[unprefixed])) {
203      angular.element.prototype[unprefixed] = fn;
204    }
205  });
206
207}]);
208
209
210//Adapted from https://gist.github.com/paulirish/1579671
211angular.module('duScroll.polyfill', [])
212.factory('polyfill', ["$window", function($window) {
213  'use strict';
214
215  var vendors = ['webkit', 'moz', 'o', 'ms'];
216
217  return function(fnName, fallback) {
218    if($window[fnName]) {
219      return $window[fnName];
220    }
221    var suffix = fnName.substr(0, 1).toUpperCase() + fnName.substr(1);
222    for(var key, i = 0; i < vendors.length; i++) {
223      key = vendors[i]+suffix;
224      if($window[key]) {
225        return $window[key];
226      }
227    }
228    return fallback;
229  };
230}]);
231
232angular.module('duScroll.requestAnimation', ['duScroll.polyfill'])
233.factory('requestAnimation', ["polyfill", "$timeout", function(polyfill, $timeout) {
234  'use strict';
235
236  var lastTime = 0;
237  var fallback = function(callback, element) {
238    var currTime = new Date().getTime();
239    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
240    var id = $timeout(function() { callback(currTime + timeToCall); },
241      timeToCall);
242    lastTime = currTime + timeToCall;
243    return id;
244  };
245
246  return polyfill('requestAnimationFrame', fallback);
247}])
248.factory('cancelAnimation', ["polyfill", "$timeout", function(polyfill, $timeout) {
249  'use strict';
250
251  var fallback = function(promise) {
252    $timeout.cancel(promise);
253  };
254
255  return polyfill('cancelAnimationFrame', fallback);
256}]);
257
258
259angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])
260.factory('spyAPI', ["$rootScope", "$timeout", "$interval", "$window", "$document", "scrollC
260ontainerAPI", "duScrollGreedy", "duScrollSpyWait", "duScrollSpyRefreshInterval", "duScrollBottomSpy", "duScrollActiveClass", function($rootScope, $timeout, $interval, $window, $document, scrollContainerAPI, duScrollGreedy, duScrollSpyWait, duScrollSpyRefreshInterval, duScrollBottomSpy, duScrollActiveClass) {
261  'use strict';
262
263  var createScrollHandler = function(context) {
264    var timer = false, queued = false;
265    var handler = function() {
266      queued = false;
267      var container = context.container,
268          containerEl = container[0],
269          containerOffset = 0,
270          bottomReached;
271
272      if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {
273        containerOffset = containerEl.getBoundingClientRect().top;
274        bottomReached = Math.round(containerEl.scrollTop + containerEl.clientHeight) >= containerEl.scrollHeight;
275      } else {
276        var documentScrollHeight = $document[0].body.scrollHeight || $document[0].documentElement.scrollHeight; // documentElement for IE11
277        bottomReached = Math.round($window.pageYOffset + $window.innerHeight) >= documentScrollHeight;
278      }
279      var compareProperty = (duScrollBottomSpy && bottomReached ? 'bottom' : 'top');
280
281      var i, currentlyActive, toBeActive, spies, spy, pos;
282      spies = context.spies;
283      currentlyActive = context.currentlyActive;
284      toBeActive = undefined;
285
286      for(i = 0; i < spies.length; i++) {
287        spy = spies[i];
288        pos = spy.getTargetPosition();
289        if (!pos || !spy.$element) continue;
290
291        if((duScrollBottomSpy && bottomReached) || (pos.top + spy.offset - containerOffset < 20 && (duScrollGreedy || pos.top*-1 + containerOffset) < pos.height)) {
292          //Find the one closest the viewport top or the page bottom if it's reached
293          if(!toBeActive || toBeActive[compareProperty] < pos[compareProperty]) {
294            toBeActive = {
295              spy: spy
296            };
297            toBeActive[compareProperty] = pos[compareProperty];
298          }
299        }
300      }
301
302      if(toBeActive) {
303        toBeActive = toBeActive.spy;
304      }
305      if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;
306      if(currentlyActive && currentlyActive.$element) {
307        currentlyActive.$element.removeClass(duScrollActiveClass);
308        $rootScope.$broadcast(
309          'duScrollspy:becameInactive',
310          currentlyActive.$element,
311          angular.element(currentlyActive.getTargetElement())
312        );
313      }
314      if(toBeActive) {
315        toBeActive.$element.addClass(duScrollActiveClass);
316        $rootScope.$broadcast(
317          'duScrollspy:becameActive',
318          toBeActive.$element,
319          angular.element(toBeActive.getTargetElement())
320        );
321      }
322      context.currentlyActive = toBeActive;
323    };
324
325    if(!duScrollSpyWait) {
326      return handler;
327    }
328
329    //Debounce for potential performance savings
330    return function() {
331      if(!timer) {
332        handler();
333        timer = $timeout(function() {
334          timer = false;
335          if(queued) {
336            handler();
337          }
338        }, duScrollSpyWait, false);
339      } else {
340        queued = true;
341      }
342    };
343  };
344
345  var contexts = {};
346
347  var createContext = function($scope) {
348    var id = $scope.$id;
349    var context = {
350      spies: []
351    };
352
353    context.handler = createScrollHandler(context);
354    contexts[id] = context;
355
356    $scope.$on('$destroy', function() {
357      destroyContext($scope);
358    });
359
360    return id;
361  };
362
363  var destroyContext = function($scope) {
364    var id = $scope.$id;
365    var context = contexts[id], container = context.container;
366    if(context.intervalPromise) {
367      $interval.cancel(context.intervalPromise);
368    }
369    if(container) {
370      container.off('scroll', context.handler);
371    }
372    delete contexts[id];
373  };
374
375  var defaultContextId = createContext($rootScope);
376
377  var getContextForScope = function(scope) {
378    if(contexts[scope.$id]) {
379      return contexts[scope.$id];
380    }
381    if(scope.$parent) {
382      return getContextForScope(scope.$parent);
383    }
384    return contexts[defaultContextId];
385  };
386
387  var getContextForSpy = function(spy) {
388    var context, contextId, scope = spy.$scope;
389    if(scope) {
390      return getContextForScope(scope);
391    }
392    //No scope, most likely destroyed
393    for(contextId in contexts) {
394      context = contexts[contextId];
395      if(context.spies.indexOf(spy) !== -1) {
396        return context;
397      }
398    }
399  };
400
401  var isElementInDocument = function(element) {
402    while (element.parentNode) {
403      element = element.parentNode;
404      if (element === document) {
405        return true;
406      }
407    }
408    return false;
409  };
410
411  var addSpy = function(spy) {
412    var context = getContextForSpy(spy);
413    if (!context) return;
414    context.spies.push(spy);
415    if (!context.container || !isElementInDocument(context.container)) {
416      if(context.container) {
417        context.container.off('scroll', context.handler);
418      }
419      context.container = scrollContainerAPI.getContainer(spy.$scope);
420      if (duScrollSpyRefreshInterval && !context.intervalPromise) {
421        context.intervalPromise = $interval(context.handler, duScrollSpyRefreshInterval, 0, false);
422      }
423      context.container.on('scroll', context.handler).triggerHandler('scroll');
424    }
425  };
426
427  var removeSpy = function(spy) {
428    var context = getContextForSpy(spy);
429    if(spy === context.currentlyActive) {
430      $rootScope.$broadcast('duScrollspy:becameInactive', context.currentlyActive.$element);
431      context.currentlyActive = null;
432    }
433    var i = context.spies.indexOf(spy);
434    if(i !== -1) {
435      context.spies.splice(i, 1);
436    }
437		spy.$element = null;
438  };
439
440  return {
441    addSpy: addSpy,
442    removeSpy: removeSpy,
443    createContext: createContext,
444    destroyContext: destroyContext,
445    getContextForScope: getContextForScope
446  };
447}]);
448
449
450angular.module('duScroll.scrollContainerAPI', [])
451.factory('scrollContainerAPI', ["$document", function($document) {
452  'use strict';
453
454  var containers = {};
455
456  var setContainer = function(scope, element) {
457    var id = scope.$id;
458    containers[id] = element;
459    return id;
460  };
461
462  var getContainerId = function(scope) {
463    if(containers[scope.$id]) {
464      return scope.$id;
465    }
466    if(scope.$parent) {
467      return getContainerId(scope.$parent);
468    }
469    return;
470  };
471
472  var getContainer = function(scope) {
473    var id = getContainerId(scope);
474    return id ? containers[id] : $document;
475  };
476
477  var removeContainer = function(scope) {
478    var id = getContainerId(scope);
479    if(id) {
480      delete containers[id];
481    }
482  };
483
484  return {
485    getContainerId:   getContainerId,
486    getContainer:     getContainer,
487    setContainer:     setContainer,
488    removeContainer:  removeContainer
489  };
490}]);
491
492
493angular.module('duScroll.smoothScroll', ['duScroll.scrollHelpers', 'duScroll.scrollContainerAPI'])
494.directive('duSmoothScroll', ["duScrollDuration", "duScrollOffset", "scrollContainerAPI", function(duScrollDuration, duScrollOffset, scrollContainerAPI) {
495  'use strict';
496
497  return {
498    link : function($scope, $element, $attr) {
499      $element.on('click', function(e) {
500        if((!$attr.href || $attr.href.indexOf('#') === -1) && $attr.duSmoothScroll === '') return;
501
502        var id = $attr.href ? $attr.href.replace(/.*(?=#[^\s]+$)/, '').substring(1) : $attr.duSmoothScroll;
503
504        var target = document.getElementById(id) || document.getElementsByName(id)[0];
505        if(!target || !target.getBoundingClientRect) return;
506
507        if (e.stopPropagation) e.stopPropagation();
508        if (e.preventDefault) e.preventDefault();
509
510        var offset    = $attr.offset ? parseInt($attr.offset, 10) : duScrollOffset;
511        var duration  = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration;
512        var container = scrollContainerAPI.getContainer($scope);
513
514        container.duScrollToElement(
515          angular.element(target),
516          isNaN(offset) ? 0 : offset,
517          isNaN(duration) ? 0 : duration
518        );
519      });
520    }
521  };
522}]);
523
524
525angular.module('duScroll.spyContext', ['duScroll.spyAPI'])
526.directive('duSpyContext', ["spyAPI", function(spyAPI) {
527  'use strict';
528
529  return {
530    restrict: 'A',
531    scope: true,
532    compile: function compile(tElement, tAttrs, transclude) {
533      return {
534        pre: function preLink($scope, iElement, iAttrs, controller) {
535          spyAPI.createContext($scope);
536        }
537      };
538    }
539  };
540}]);
541
542
543angular.module('duScroll.scrollContainer', ['duScroll.scrollContainerAPI'])
544.directive('duScrollContainer', ["scrollContainerAPI", function(scrollContainerAPI){
545  'use strict';
546
547  return {
548    restrict: 'A',
549    scope: true,
550    compile: function compile(tElement, tAttrs, transclude) {
551      return {
552        pre: function preLink($scope, iElement, iAttrs, controller) {
553          iAttrs.$observe('duScrollContainer', function(element) {
554            if(angular.isString(element)) {
555              element = document.getElementById(element);
556            }
557
558            element = (angular.isElement(element) ? angular.element(element) : iElement);
559            scrollContainerAPI.setContainer($scope, element);
560            $scope.$on('$destroy', function() {
561              scrollContainerAPI.removeContainer($scope);
562            });
563          });
564        }
565      };
566    }
567  };
568}]);
569
570
571angular.module('duScroll.scrollspy', ['duScroll.spyAPI'])
572.directive('duScrollspy', ["spyAPI", "duScrollOffset", "$timeout", "$rootScope", function(spyAPI, duScrollOffset, $timeout, $rootScope) {
573  'use strict';
574
575  var Spy = function(targetElementOrId, $scope, $element, offset) {
576    if(angular.isElement(targetElementOrId)) {
577      this.target = targetElementOrId;
578    } else if(angular.isString(targetElementOrId)) {
579      this.targetId = targetElementOrId;
580    }
581    this.$scope = $scope;
582    this.$element = $element;
583    this.offset = offset;
584  };
585
586  Spy.prototype.getTargetElement = function() {
587    if (!this.target && this.targetId) {
588      this.target = document.getElementById(this.targetId) || document.getElementsByName(this.targetId)[0];
589    }
590    return this.target;
591  };
592
593  Spy.prototype.getTargetPosition = function() {
594    var target = this.getTargetElement();
595    if(target) {
596      return target.getBoundingClientRect();
597    }
598  };
599
600  Spy.prototype.flushTargetCache = function() {
601    if(this.targetId) {
602      this.target = undefined;
603    }
604  };
605
606  return {
607    link: function ($scope, $element, $attr) {
608      var href = $attr.ngHref || $attr.href;
609      var targetId;
610
611      if (href && href.indexOf('#') !== -1) {
612        targetId = href.replace(/.*(?=#[^\s]+$)/, '').substring(1);
613      } else if($attr.duScrollspy) {
614        targetId = $attr.duScrollspy;
615      } else if($attr.duSmoothScroll) {
616        targetId = $attr.duSmoothScroll;
617      }
618      if(!targetId) return;
619
620      // Run this in the next execution loop so that the scroll context has a chance
621      // to initialize
622      var timeoutPromise = $timeout(function() {
623        var spy = new Spy(targetId, $scope, $element, -($attr.offset ? parseInt($attr.offset, 10) : duScrollOffset));
624        spyAPI.addSpy(spy);
625
626        $scope.$on('$locationChangeSuccess', spy.flushTargetCache.bind(spy));
627        var deregisterOnStateChange = $rootScope.$on('$stateChangeSuccess', spy.flushTargetCache.bind(spy));
628        $scope.$on('$destroy', function() {
629          spyAPI.removeSpy(spy);
630          deregisterOnStateChange();
631        });
632      }, 0, false);
633      $scope.$on('$destroy', function() {$timeout.cancel(timeoutPromise);});
634    }
635  };
636}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.