123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /**
- * angular-strap
- * @version v2.3.9 - 2016-06-10
- * @link http://mgcrea.github.io/angular-strap
- * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
- * @license MIT License, http://www.opensource.org/licenses/MIT
- */
- 'use strict';
- angular.module('mgcrea.ngStrap.scrollspy', [ 'mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$scrollspy', function() {
- var spies = this.$$spies = {};
- var defaults = this.defaults = {
- debounce: 150,
- throttle: 100,
- offset: 100
- };
- this.$get = [ '$window', '$document', '$rootScope', 'dimensions', 'debounce', 'throttle', function($window, $document, $rootScope, dimensions, debounce, throttle) {
- var windowEl = angular.element($window);
- var docEl = angular.element($document.prop('documentElement'));
- var bodyEl = angular.element($window.document.body);
- function nodeName(element, name) {
- return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
- }
- function ScrollSpyFactory(config) {
- var options = angular.extend({}, defaults, config);
- if (!options.element) options.element = bodyEl;
- var isWindowSpy = nodeName(options.element, 'body');
- var scrollEl = isWindowSpy ? windowEl : options.element;
- var scrollId = isWindowSpy ? 'window' : options.id;
- if (spies[scrollId]) {
- spies[scrollId].$$count++;
- return spies[scrollId];
- }
- var $scrollspy = {};
- var unbindViewContentLoaded;
- var unbindIncludeContentLoaded;
- var trackedElements = $scrollspy.$trackedElements = [];
- var sortedElements = [];
- var activeTarget;
- var debouncedCheckPosition;
- var throttledCheckPosition;
- var debouncedCheckOffsets;
- var viewportHeight;
- var scrollTop;
- $scrollspy.init = function() {
- this.$$count = 1;
- debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
- throttledCheckPosition = throttle(this.checkPosition, options.throttle);
- scrollEl.on('click', this.checkPositionWithEventLoop);
- windowEl.on('resize', debouncedCheckPosition);
- scrollEl.on('scroll', throttledCheckPosition);
- debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
- unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
- unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
- debouncedCheckOffsets();
- if (scrollId) {
- spies[scrollId] = $scrollspy;
- }
- };
- $scrollspy.destroy = function() {
- this.$$count--;
- if (this.$$count > 0) {
- return;
- }
- scrollEl.off('click', this.checkPositionWithEventLoop);
- windowEl.off('resize', debouncedCheckPosition);
- scrollEl.off('scroll', throttledCheckPosition);
- unbindViewContentLoaded();
- unbindIncludeContentLoaded();
- if (scrollId) {
- delete spies[scrollId];
- }
- };
- $scrollspy.checkPosition = function() {
- if (!sortedElements.length) return;
- scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;
- viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));
- if (scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
- return $scrollspy.$activateElement(sortedElements[0]);
- }
- for (var i = sortedElements.length; i--; ) {
- if (angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;
- if (activeTarget === sortedElements[i].target) continue;
- if (scrollTop < sortedElements[i].offsetTop) continue;
- if (sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
- return $scrollspy.$activateElement(sortedElements[i]);
- }
- };
- $scrollspy.checkPositionWithEventLoop = function() {
- setTimeout($scrollspy.checkPosition, 1);
- };
- $scrollspy.$activateElement = function(element) {
- if (activeTarget) {
- var activeElement = $scrollspy.$getTrackedElement(activeTarget);
- if (activeElement) {
- activeElement.source.removeClass('active');
- if (nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
- activeElement.source.parent().parent().removeClass('active');
- }
- }
- }
- activeTarget = element.target;
- element.source.addClass('active');
- if (nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
- element.source.parent().parent().addClass('active');
- }
- };
- $scrollspy.$getTrackedElement = function(target) {
- return trackedElements.filter(function(obj) {
- return obj.target === target;
- })[0];
- };
- $scrollspy.checkOffsets = function() {
- angular.forEach(trackedElements, function(trackedElement) {
- var targetElement = document.querySelector(trackedElement.target);
- trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
- if (options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
- });
- sortedElements = trackedElements.filter(function(el) {
- return el.offsetTop !== null;
- }).sort(function(a, b) {
- return a.offsetTop - b.offsetTop;
- });
- debouncedCheckPosition();
- };
- $scrollspy.trackElement = function(target, source) {
- trackedElements.push({
- target: target,
- source: source
- });
- };
- $scrollspy.untrackElement = function(target, source) {
- var toDelete;
- for (var i = trackedElements.length; i--; ) {
- if (trackedElements[i].target === target && trackedElements[i].source === source) {
- toDelete = i;
- break;
- }
- }
- trackedElements.splice(toDelete, 1);
- };
- $scrollspy.activate = function(i) {
- trackedElements[i].addClass('active');
- };
- $scrollspy.init();
- return $scrollspy;
- }
- return ScrollSpyFactory;
- } ];
- }).directive('bsScrollspy', [ '$rootScope', 'debounce', 'dimensions', '$scrollspy', function($rootScope, debounce, dimensions, $scrollspy) {
- return {
- restrict: 'EAC',
- link: function postLink(scope, element, attr) {
- var options = {
- scope: scope
- };
- angular.forEach([ 'offset', 'target' ], function(key) {
- if (angular.isDefined(attr[key])) options[key] = attr[key];
- });
- var scrollspy = $scrollspy(options);
- scrollspy.trackElement(options.target, element);
- scope.$on('$destroy', function() {
- if (scrollspy) {
- scrollspy.untrackElement(options.target, element);
- scrollspy.destroy();
- }
- options = null;
- scrollspy = null;
- });
- }
- };
- } ]).directive('bsScrollspyList', [ '$rootScope', 'debounce', 'dimensions', '$scrollspy', function($rootScope, debounce, dimensions, $scrollspy) {
- return {
- restrict: 'A',
- compile: function postLink(element, attr) {
- var children = element[0].querySelectorAll('li > a[href]');
- angular.forEach(children, function(child) {
- var childEl = angular.element(child);
- childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
- });
- }
- };
- } ]);
|