diff --git a/angular-scroll.js b/angular-scroll.js index 041578b..9d22e9b 100644 --- a/angular-scroll.js +++ b/angular-scroll.js @@ -16,14 +16,21 @@ angular.module('duScroll', [ 'duScroll.scrollContainer', 'duScroll.spyContext', 'duScroll.scrollHelpers' -]).value('duScrollDuration', 350) +]) + //Default animation duration for smoothScroll directive + .value('duScrollDuration', 350) + //Scrollspy debounce interval, set to 0 to disable .value('duScrollSpyWait', 100) + //Wether or not multiple scrollspies can be active at once .value('duScrollGreedy', false) + //Default offset for smoothScroll directive + .value('duScrollOffset', 0) + //Default easing function for scroll animation .value('duScrollEasing', duScrollDefaultEasing); angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation']) -.run(["$window", "$q", "cancelAnimation", "requestAnimation", "duScrollEasing", function($window, $q, cancelAnimation, requestAnimation, duScrollEasing) { +.run(["$window", "$q", "cancelAnimation", "requestAnimation", "duScrollEasing", "duScrollDuration", "duScrollOffset", function($window, $q, cancelAnimation, requestAnimation, duScrollEasing, duScrollDuration, duScrollOffset) { 'use strict'; var proto = angular.element.prototype; @@ -123,7 +130,10 @@ angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation']) proto.scrollToElement = function(target, offset, duration, easing) { var el = unwrap(this); - var top = this.scrollTop() + unwrap(target).getBoundingClientRect().top - (offset || 0); + if(!angular.isNumber(offset) || isNaN(offset)) { + offset = duScrollOffset; + } + var top = this.scrollTop() + unwrap(target).getBoundingClientRect().top - offset; if(isElement(el)) { top -= el.getBoundingClientRect().top; } @@ -153,6 +163,18 @@ angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation']) } }; + proto.scrollToElementAnimated = function(target, offset, duration, easing) { + return this.scrollToElement(target, offset, duration || duScrollDuration, easing); + }; + + proto.scrollTopAnimated = function(top, duration, easing) { + return this.scrollTop(top, duration || duScrollDuration, easing); + }; + + proto.scrollLeftAnimated = function(left, duration, easing) { + return this.scrollLeft(left, duration || duScrollDuration, easing); + }; + //Add duration and easing functionality to existing jQuery getter/setters var overloadScrollPos = function(superFn, overloadFn) { return function(value, duration, easing) { @@ -226,7 +248,7 @@ angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI']) var timer = false, queued = false; var handler = function() { queued = false; - var container = context.container, + var container = context.container, containerEl = container[0], containerOffset = 0; @@ -267,7 +289,7 @@ angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI']) } context.currentlyActive = toBeActive; }; - + if(!duScrollSpyWait) { return handler; } @@ -281,7 +303,7 @@ angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI']) if(queued) { handler(); } - }, duScrollSpyWait); + }, duScrollSpyWait, false); } else { queued = true; } @@ -295,10 +317,10 @@ angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI']) var context = { spies: [] }; - + context.handler = createScrollHandler(context); contexts[id] = context; - + $scope.$on('$destroy', function() { destroyContext($scope); }); @@ -353,7 +375,8 @@ angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI']) var addSpy = function(spy) { var context = getContextForSpy(spy); - getContextForSpy(spy).spies.push(spy); + if (!context) return; + context.spies.push(spy); if (!context.container || !isElementInDocument(context.container)) { if(context.container) { context.container.off('scroll', context.handler); @@ -376,7 +399,7 @@ angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI']) return { addSpy: addSpy, - removeSpy: removeSpy, + removeSpy: removeSpy, createContext: createContext, destroyContext: destroyContext, getContextForScope: getContextForScope @@ -428,21 +451,22 @@ angular.module('duScroll.scrollContainerAPI', []) angular.module('duScroll.smoothScroll', ['duScroll.scrollHelpers', 'duScroll.scrollContainerAPI']) -.directive('duSmoothScroll', ["duScrollDuration", "scrollContainerAPI", function(duScrollDuration, scrollContainerAPI){ +.directive('duSmoothScroll', ["duScrollDuration", "duScrollOffset", "scrollContainerAPI", function(duScrollDuration, duScrollOffset, scrollContainerAPI) { 'use strict'; return { - link : function($scope, $element, $attr){ - $element.on('click', function(e){ + link : function($scope, $element, $attr) { + $element.on('click', function(e) { if(!$attr.href || $attr.href.indexOf('#') === -1) return; + var target = document.getElementById($attr.href.replace(/.*(?=#[^\s]+$)/, '').substring(1)); if(!target || !target.getBoundingClientRect) return; if (e.stopPropagation) e.stopPropagation(); if (e.preventDefault) e.preventDefault(); - var offset = ($attr.offset ? parseInt($attr.offset, 10) : 0); - var duration = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration; + var offset = $attr.offset ? parseInt($attr.offset, 10) : duScrollOffset; + var duration = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration; var container = scrollContainerAPI.getContainer($scope); container.scrollToElement( @@ -503,7 +527,7 @@ angular.module('duScroll.scrollContainer', ['duScroll.scrollContainerAPI']) angular.module('duScroll.scrollspy', ['duScroll.spyAPI']) -.directive('duScrollspy', ["spyAPI", "$timeout", "$rootScope", function(spyAPI, $timeout, $rootScope) { +.directive('duScrollspy', ["spyAPI", "duScrollOffset", "$timeout", "$rootScope", function(spyAPI, duScrollOffset, $timeout, $rootScope) { 'use strict'; var Spy = function(targetElementOrId, $element, offset) { @@ -551,7 +575,7 @@ angular.module('duScroll.scrollspy', ['duScroll.spyAPI']) // Run this in the next execution loop so that the scroll context has a chance // to initialize $timeout(function() { - var spy = new Spy(targetId, $element, -($attr.offset ? parseInt($attr.offset, 10) : 0)); + var spy = new Spy(targetId, $element, -($attr.offset ? parseInt($attr.offset, 10) : duScrollOffset)); spyAPI.addSpy(spy); $scope.$on('$destroy', function() { @@ -559,7 +583,7 @@ angular.module('duScroll.scrollspy', ['duScroll.spyAPI']) }); $scope.$on('$locationChangeSuccess', spy.flushTargetCache.bind(spy)); $rootScope.$on('$stateChangeSuccess', spy.flushTargetCache.bind(spy)); - }, 0); + }, 0, false); } }; }]); diff --git a/angular-scroll.min.js b/angular-scroll.min.js index 5bff638..4a4eecf 100644 --- a/angular-scroll.min.js +++ b/angular-scroll.min.js @@ -1,2 +1,2 @@ -var duScrollDefaultEasing=function(e){"use strict";return.5>e?Math.pow(2*e,2)/2:1-Math.pow(2*(1-e),2)/2};angular.module("duScroll",["duScroll.scrollspy","duScroll.smoothScroll","duScroll.scrollContainer","duScroll.spyContext","duScroll.scrollHelpers"]).value("duScrollDuration",350).value("duScrollSpyWait",100).value("duScrollGreedy",!1).value("duScrollEasing",duScrollDefaultEasing),angular.module("duScroll.scrollHelpers",["duScroll.requestAnimation"]).run(["$window","$q","cancelAnimation","requestAnimation","duScrollEasing",function(e,t,n,r,o){"use strict";var l=angular.element.prototype,i=function(e){return"undefined"!=typeof HTMLDocument&&e instanceof HTMLDocument||e.nodeType&&e.nodeType===e.DOCUMENT_NODE},u=function(e){return"undefined"!=typeof HTMLElement&&e instanceof HTMLElement||e.nodeType&&e.nodeType===e.ELEMENT_NODE},c=function(e){return u(e)||i(e)?e:e[0]};l.scrollTo=function(t,n,r){var o;if(angular.isElement(t)?o=this.scrollToElement:r&&(o=this.scrollToAnimated),o)return o.apply(this,arguments);var l=c(this);return i(l)?e.scrollTo(t,n):(l.scrollLeft=t,void(l.scrollTop=n))};var a,s;l.scrollToAnimated=function(e,l,i,u){i&&!u&&(u=o);var c=this.scrollLeft(),d=this.scrollTop(),f=Math.round(e-c),p=Math.round(l-d),m=null,g=this,v="scroll mousedown mousewheel touchmove keydown",y=function(e){(!e||e.which>0)&&(g.unbind(v,y),n(a),s.reject(),a=null)};if(a&&y(),s=t.defer(),!f&&!p)return s.resolve(),s.promise;var h=function(e){null===m&&(m=e);var t=e-m,n=t>=i?1:u(t/i);g.scrollTo(c+Math.ceil(f*n),d+Math.ceil(p*n)),1>n?a=r(h):(g.unbind(v,y),a=null,s.resolve())};return g.scrollTo(c,d),g.bind(v,y),a=r(h),s.promise},l.scrollToElement=function(e,t,n,r){var o=c(this),l=this.scrollTop()+c(e).getBoundingClientRect().top-(t||0);return u(o)&&(l-=o.getBoundingClientRect().top),this.scrollTo(0,l,n,r)};var d={scrollLeft:function(t,n,r){if(angular.isNumber(t))return this.scrollTo(t,this.scrollTop(),n,r);var o=c(this);return i(o)?e.scrollX||document.documentElement.scrollLeft||document.body.scrollLeft:o.scrollLeft},scrollTop:function(t,n,r){if(angular.isNumber(t))return this.scrollTo(this.scrollTop(),t,n,r);var o=c(this);return i(o)?e.scrollY||document.documentElement.scrollTop||document.body.scrollTop:o.scrollTop}},f=function(e,t){return function(n,r){return r?t.apply(this,arguments):e.apply(this,arguments)}};for(var p in d)l[p]=l[p]?f(l[p],d[p]):d[p]}]),angular.module("duScroll.polyfill",[]).factory("polyfill",["$window",function(e){"use strict";var t=["webkit","moz","o","ms"];return function(n,r){if(e[n])return e[n];for(var o,l=n.substr(0,1).toUpperCase()+n.substr(1),i=0;ie?Math.pow(2*e,2)/2:1-Math.pow(2*(1-e),2)/2};angular.module("duScroll",["duScroll.scrollspy","duScroll.smoothScroll","duScroll.scrollContainer","duScroll.spyContext","duScroll.scrollHelpers"]).value("duScrollDuration",350).value("duScrollSpyWait",100).value("duScrollGreedy",!1).value("duScrollOffset",0).value("duScrollEasing",duScrollDefaultEasing),angular.module("duScroll.scrollHelpers",["duScroll.requestAnimation"]).run(["$window","$q","cancelAnimation","requestAnimation","duScrollEasing","duScrollDuration","duScrollOffset",function(e,t,n,r,o,l,i){"use strict";var u=angular.element.prototype,c=function(e){return"undefined"!=typeof HTMLDocument&&e instanceof HTMLDocument||e.nodeType&&e.nodeType===e.DOCUMENT_NODE},a=function(e){return"undefined"!=typeof HTMLElement&&e instanceof HTMLElement||e.nodeType&&e.nodeType===e.ELEMENT_NODE},s=function(e){return a(e)||c(e)?e:e[0]};u.scrollTo=function(t,n,r){var o;if(angular.isElement(t)?o=this.scrollToElement:r&&(o=this.scrollToAnimated),o)return o.apply(this,arguments);var l=s(this);return c(l)?e.scrollTo(t,n):(l.scrollLeft=t,void(l.scrollTop=n))};var d,f;u.scrollToAnimated=function(e,l,i,u){i&&!u&&(u=o);var c=this.scrollLeft(),a=this.scrollTop(),s=Math.round(e-c),p=Math.round(l-a),m=null,g=this,v="scroll mousedown mousewheel touchmove keydown",h=function(e){(!e||e.which>0)&&(g.unbind(v,h),n(d),f.reject(),d=null)};if(d&&h(),f=t.defer(),!s&&!p)return f.resolve(),f.promise;var y=function(e){null===m&&(m=e);var t=e-m,n=t>=i?1:u(t/i);g.scrollTo(c+Math.ceil(s*n),a+Math.ceil(p*n)),1>n?d=r(y):(g.unbind(v,h),d=null,f.resolve())};return g.scrollTo(c,a),g.bind(v,h),d=r(y),f.promise},u.scrollToElement=function(e,t,n,r){var o=s(this);(!angular.isNumber(t)||isNaN(t))&&(t=i);var l=this.scrollTop()+s(e).getBoundingClientRect().top-t;return a(o)&&(l-=o.getBoundingClientRect().top),this.scrollTo(0,l,n,r)};var p={scrollLeft:function(t,n,r){if(angular.isNumber(t))return this.scrollTo(t,this.scrollTop(),n,r);var o=s(this);return c(o)?e.scrollX||document.documentElement.scrollLeft||document.body.scrollLeft:o.scrollLeft},scrollTop:function(t,n,r){if(angular.isNumber(t))return this.scrollTo(this.scrollTop(),t,n,r);var o=s(this);return c(o)?e.scrollY||document.documentElement.scrollTop||document.body.scrollTop:o.scrollTop}};u.scrollToElementAnimated=function(e,t,n,r){return this.scrollToElement(e,t,n||l,r)},u.scrollTopAnimated=function(e,t,n){return this.scrollTop(e,t||l,n)},u.scrollLeftAnimated=function(e,t,n){return this.scrollLeft(e,t||l,n)};var m=function(e,t){return function(n,r){return r?t.apply(this,arguments):e.apply(this,arguments)}};for(var g in p)u[g]=u[g]?m(u[g],p[g]):p[g]}]),angular.module("duScroll.polyfill",[]).factory("polyfill",["$window",function(e){"use strict";var t=["webkit","moz","o","ms"];return function(n,r){if(e[n])return e[n];for(var o,l=n.substr(0,1).toUpperCase()+n.substr(1),i=0;i 0) {\n el.unbind(cancelOnEvents, cancelScrollAnimation);\n cancelAnimation(scrollAnimation);\n deferred.reject();\n scrollAnimation = null;\n }\n };\n\n if(scrollAnimation) {\n cancelScrollAnimation();\n }\n deferred = $q.defer();\n\n if(!deltaLeft && !deltaTop) {\n deferred.resolve();\n return deferred.promise;\n }\n\n var animationStep = function(timestamp) {\n if (startTime === null) {\n startTime = timestamp;\n }\n\n var progress = timestamp - startTime;\n var percent = (progress >= duration ? 1 : easing(progress/duration));\n\n el.scrollTo(\n startLeft + Math.ceil(deltaLeft * percent),\n startTop + Math.ceil(deltaTop * percent)\n );\n if(percent < 1) {\n scrollAnimation = requestAnimation(animationStep);\n } else {\n el.unbind(cancelOnEvents, cancelScrollAnimation);\n scrollAnimation = null;\n deferred.resolve();\n }\n };\n\n //Fix random mobile safari bug when scrolling to top by hitting status bar\n el.scrollTo(startLeft, startTop);\n\n el.bind(cancelOnEvents, cancelScrollAnimation);\n\n scrollAnimation = requestAnimation(animationStep);\n return deferred.promise;\n };\n\n proto.scrollToElement = function(target, offset, duration, easing) {\n var el = unwrap(this);\n var top = this.scrollTop() + unwrap(target).getBoundingClientRect().top - (offset || 0);\n if(isElement(el)) {\n top -= el.getBoundingClientRect().top;\n }\n return this.scrollTo(0, top, duration, easing);\n };\n\n var overloaders = {\n scrollLeft: function(value, duration, easing) {\n if(angular.isNumber(value)) {\n return this.scrollTo(value, this.scrollTop(), duration, easing);\n }\n var el = unwrap(this);\n if(isDocument(el)) {\n return $window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft;\n }\n return el.scrollLeft;\n },\n scrollTop: function(value, duration, easing) {\n if(angular.isNumber(value)) {\n return this.scrollTo(this.scrollTop(), value, duration, easing);\n }\n var el = unwrap(this);\n if(isDocument(el)) {\n return $window.scrollY || document.documentElement.scrollTop || document.body.scrollTop;\n }\n return el.scrollTop;\n }\n };\n\n //Add duration and easing functionality to existing jQuery getter/setters\n var overloadScrollPos = function(superFn, overloadFn) {\n return function(value, duration, easing) {\n if(duration) {\n return overloadFn.apply(this, arguments);\n }\n return superFn.apply(this, arguments);\n };\n };\n\n for(var methodName in overloaders) {\n proto[methodName] = (proto[methodName] ? overloadScrollPos(proto[methodName], overloaders[methodName]) : overloaders[methodName]);\n }\n});\n","//Adapted from https://gist.github.com/paulirish/1579671\nangular.module('duScroll.polyfill', [])\n.factory('polyfill', function($window) {\n 'use strict';\n\n var vendors = ['webkit', 'moz', 'o', 'ms'];\n\n return function(fnName, fallback) {\n if($window[fnName]) {\n return $window[fnName];\n }\n var suffix = fnName.substr(0, 1).toUpperCase() + fnName.substr(1);\n for(var key, i = 0; i < vendors.length; i++) {\n key = vendors[i]+suffix;\n if($window[key]) {\n return $window[key];\n }\n }\n return fallback;\n };\n});\n\nangular.module('duScroll.requestAnimation', ['duScroll.polyfill'])\n.factory('requestAnimation', function(polyfill, $timeout) {\n 'use strict';\n\n var lastTime = 0;\n var fallback = function(callback, element) {\n var currTime = new Date().getTime();\n var timeToCall = Math.max(0, 16 - (currTime - lastTime));\n var id = $timeout(function() { callback(currTime + timeToCall); },\n timeToCall);\n lastTime = currTime + timeToCall;\n return id;\n };\n \n return polyfill('requestAnimationFrame', fallback);\n})\n.factory('cancelAnimation', function(polyfill, $timeout) {\n 'use strict';\n\n var fallback = function(promise) {\n $timeout.cancel(promise);\n };\n\n return polyfill('cancelAnimationFrame', fallback);\n});\n","angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])\n.factory('spyAPI', function($rootScope, $timeout, scrollContainerAPI, duScrollGreedy, duScrollSpyWait) {\n 'use strict';\n\n var createScrollHandler = function(context) {\n var timer = false, queued = false;\n var handler = function() {\n queued = false;\n var container = context.container, \n containerEl = container[0],\n containerOffset = 0;\n\n if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {\n containerOffset = containerEl.getBoundingClientRect().top;\n }\n\n var i, currentlyActive, toBeActive, spies, spy, pos;\n spies = context.spies;\n currentlyActive = context.currentlyActive;\n toBeActive = undefined;\n\n for(i = 0; i < spies.length; i++) {\n spy = spies[i];\n pos = spy.getTargetPosition();\n if (!pos) continue;\n\n if(pos.top + spy.offset - containerOffset < 20 && (pos.top*-1 + containerOffset) < pos.height) {\n if(!toBeActive || toBeActive.top < pos.top) {\n toBeActive = {\n top: pos.top,\n spy: spy\n };\n }\n }\n }\n if(toBeActive) {\n toBeActive = toBeActive.spy;\n }\n if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;\n if(currentlyActive) {\n currentlyActive.$element.removeClass('active');\n $rootScope.$broadcast('duScrollspy:becameInactive', currentlyActive.$element);\n }\n if(toBeActive) {\n toBeActive.$element.addClass('active');\n $rootScope.$broadcast('duScrollspy:becameActive', toBeActive.$element);\n }\n context.currentlyActive = toBeActive;\n };\n \n if(!duScrollSpyWait) {\n return handler;\n }\n\n //Debounce for potential performance savings\n return function() {\n if(!timer) {\n handler();\n timer = $timeout(function() {\n timer = false;\n if(queued) {\n handler();\n }\n }, duScrollSpyWait);\n } else {\n queued = true;\n }\n };\n };\n\n var contexts = {};\n\n var createContext = function($scope) {\n var id = $scope.$id;\n var context = {\n spies: []\n };\n \n context.handler = createScrollHandler(context);\n contexts[id] = context;\n \n $scope.$on('$destroy', function() {\n destroyContext($scope);\n });\n\n return id;\n };\n\n var destroyContext = function($scope) {\n var id = $scope.$id;\n var context = contexts[id], container = context.container;\n if(container) {\n container.off('scroll', context.handler);\n }\n delete contexts[id];\n };\n\n var defaultContextId = createContext($rootScope);\n\n var getContextForScope = function(scope) {\n if(contexts[scope.$id]) {\n return contexts[scope.$id];\n }\n if(scope.$parent) {\n return getContextForScope(scope.$parent);\n }\n return contexts[defaultContextId];\n };\n\n var getContextForSpy = function(spy) {\n var context, contextId, scope = spy.$element.scope();\n if(scope) {\n return getContextForScope(scope);\n }\n //No scope, most likely destroyed\n for(contextId in contexts) {\n context = contexts[contextId];\n if(context.spies.indexOf(spy) !== -1) {\n return context;\n }\n }\n };\n\n var isElementInDocument = function(element) {\n while (element.parentNode) {\n element = element.parentNode;\n if (element === document) {\n return true;\n }\n }\n return false;\n };\n\n var addSpy = function(spy) {\n var context = getContextForSpy(spy);\n getContextForSpy(spy).spies.push(spy);\n if (!context.container || !isElementInDocument(context.container)) {\n if(context.container) {\n context.container.off('scroll', context.handler);\n }\n context.container = scrollContainerAPI.getContainer(spy.$element.scope());\n context.container.on('scroll', context.handler).triggerHandler('scroll');\n }\n };\n\n var removeSpy = function(spy) {\n var context = getContextForSpy(spy);\n if(spy === context.currentlyActive) {\n context.currentlyActive = null;\n }\n var i = context.spies.indexOf(spy);\n if(i !== -1) {\n context.spies.splice(i, 1);\n }\n };\n\n return {\n addSpy: addSpy,\n removeSpy: removeSpy, \n createContext: createContext,\n destroyContext: destroyContext,\n getContextForScope: getContextForScope\n };\n});\n","angular.module('duScroll.scrollContainerAPI', [])\n.factory('scrollContainerAPI', function($document) {\n 'use strict';\n\n var containers = {};\n\n var setContainer = function(scope, element) {\n var id = scope.$id;\n containers[id] = element;\n return id;\n };\n\n var getContainerId = function(scope) {\n if(containers[scope.$id]) {\n return scope.$id;\n }\n if(scope.$parent) {\n return getContainerId(scope.$parent);\n }\n return;\n };\n\n var getContainer = function(scope) {\n var id = getContainerId(scope);\n return id ? containers[id] : $document;\n };\n\n var removeContainer = function(scope) {\n var id = getContainerId(scope);\n if(id) {\n delete containers[id];\n }\n };\n\n return {\n getContainerId: getContainerId, \n getContainer: getContainer, \n setContainer: setContainer,\n removeContainer: removeContainer\n };\n});\n","angular.module('duScroll.smoothScroll', ['duScroll.scrollHelpers', 'duScroll.scrollContainerAPI'])\n.directive('duSmoothScroll', function(duScrollDuration, scrollContainerAPI){\n 'use strict';\n\n return {\n link : function($scope, $element, $attr){\n $element.on('click', function(e){\n if(!$attr.href || $attr.href.indexOf('#') === -1) return;\n var target = document.getElementById($attr.href.replace(/.*(?=#[^\\s]+$)/, '').substring(1));\n if(!target || !target.getBoundingClientRect) return;\n \n if (e.stopPropagation) e.stopPropagation();\n if (e.preventDefault) e.preventDefault();\n\n var offset = ($attr.offset ? parseInt($attr.offset, 10) : 0);\n var duration = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration;\n var container = scrollContainerAPI.getContainer($scope);\n\n container.scrollToElement(\n angular.element(target), \n isNaN(offset) ? 0 : offset, \n isNaN(duration) ? 0 : duration\n );\n });\n }\n };\n});\n","angular.module('duScroll.spyContext', ['duScroll.spyAPI'])\n.directive('duSpyContext', function(spyAPI) {\n 'use strict';\n\n return {\n restrict: 'A',\n scope: true,\n compile: function compile(tElement, tAttrs, transclude) {\n return {\n pre: function preLink($scope, iElement, iAttrs, controller) {\n spyAPI.createContext($scope);\n }\n };\n }\n };\n});\n","angular.module('duScroll.scrollContainer', ['duScroll.scrollContainerAPI'])\n.directive('duScrollContainer', function(scrollContainerAPI){\n 'use strict';\n\n return {\n restrict: 'A',\n scope: true,\n compile: function compile(tElement, tAttrs, transclude) {\n return {\n pre: function preLink($scope, iElement, iAttrs, controller) {\n iAttrs.$observe('duScrollContainer', function(element) {\n if(angular.isString(element)) {\n element = document.getElementById(element);\n }\n\n element = (angular.isElement(element) ? angular.element(element) : iElement);\n scrollContainerAPI.setContainer($scope, element);\n $scope.$on('$destroy', function() {\n scrollContainerAPI.removeContainer($scope);\n });\n });\n }\n };\n }\n };\n});\n","angular.module('duScroll.scrollspy', ['duScroll.spyAPI'])\n.directive('duScrollspy', function(spyAPI, $timeout, $rootScope) {\n 'use strict';\n\n var Spy = function(targetElementOrId, $element, offset) {\n if(angular.isElement(targetElementOrId)) {\n this.target = targetElementOrId;\n } else if(angular.isString(targetElementOrId)) {\n this.targetId = targetElementOrId;\n }\n this.$element = $element;\n this.offset = offset;\n };\n\n Spy.prototype.getTargetElement = function() {\n if (!this.target && this.targetId) {\n this.target = document.getElementById(this.targetId);\n }\n return this.target;\n };\n\n Spy.prototype.getTargetPosition = function() {\n var target = this.getTargetElement();\n if(target) {\n return target.getBoundingClientRect();\n }\n };\n\n Spy.prototype.flushTargetCache = function() {\n if(this.targetId) {\n this.target = undefined;\n }\n };\n\n return {\n link: function ($scope, $element, $attr) {\n var href = $attr.ngHref || $attr.href;\n var targetId;\n\n if (href && href.indexOf('#') !== -1) {\n targetId = href.replace(/.*(?=#[^\\s]+$)/, '').substring(1);\n } else if($attr.duScrollspy) {\n targetId = $attr.duScrollspy;\n }\n if(!targetId) return;\n\n // Run this in the next execution loop so that the scroll context has a chance\n // to initialize\n $timeout(function() {\n var spy = new Spy(targetId, $element, -($attr.offset ? parseInt($attr.offset, 10) : 0));\n spyAPI.addSpy(spy);\n\n $scope.$on('$destroy', function() {\n spyAPI.removeSpy(spy);\n });\n $scope.$on('$locationChangeSuccess', spy.flushTargetCache.bind(spy));\n $rootScope.$on('$stateChangeSuccess', spy.flushTargetCache.bind(spy));\n }, 0);\n }\n };\n});\n"],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"file":"angular-scroll.min.js","sources":["module.js","helpers.js","request-animation.js","spy-api.js","scroll-container-api.js","smooth-scroll.js","spy-context.js","scroll-container.js","scrollspy.js"],"names":[],"mappings":"AAGA,GAAA,uBAAA,SAAA,GACA,YAEA,OAAA,GAAA,EACA,KAAA,IAAA,EAAA,EAAA,GAAA,EAEA,EAAA,KAAA,IAAA,GAAA,EAAA,GAAA,GAAA,EAGA,SAAA,OAAA,YACA,qBACA,wBACA,2BACA,sBACA,2BAGA,MAAA,mBAAA,KAEA,MAAA,kBAAA,KAEA,MAAA,kBAAA,GAEA,MAAA,iBAAA,GAEA,MAAA,iBAAA,uBC5BA,QAAA,OAAA,0BAAA,8BACA,KAAA,UAAA,KAAA,kBAAA,mBAAA,iBAAA,mBAAA,iBAAA,SAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,GACA,YAEA,IAAA,GAAA,QAAA,QAAA,UAEA,EAAA,SAAA,GACA,MAAA,mBAAA,eAAA,YAAA,eAAA,EAAA,UAAA,EAAA,WAAA,EAAA,eAGA,EAAA,SAAA,GACA,MAAA,mBAAA,cAAA,YAAA,cAAA,EAAA,UAAA,EAAA,WAAA,EAAA,cAGA,EAAA,SAAA,GACA,MAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GAGA,GAAA,SAAA,SAAA,EAAA,EAAA,GACA,GAAA,EAMA,IALA,QAAA,UAAA,GACA,EAAA,KAAA,gBACA,IACA,EAAA,KAAA,kBAEA,EACA,MAAA,GAAA,MAAA,KAAA,UAEA,IAAA,GAAA,EAAA,KACA,OAAA,GAAA,GACA,EAAA,SAAA,EAAA,IAEA,EAAA,WAAA,OACA,EAAA,UAAA,IAGA,IAAA,GAAA,CACA,GAAA,iBAAA,SAAA,EAAA,EAAA,EAAA,GACA,IAAA,IACA,EAAA,EAEA,IAAA,GAAA,KAAA,aACA,EAAA,KAAA,YACA,EAAA,KAAA,MAAA,EAAA,GACA,EAAA,KAAA,MAAA,EAAA,GAEA,EAAA,KACA,EAAA,KAEA,EAAA,gDACA,EAAA,SAAA,KACA,GAAA,EAAA,MAAA,KACA,EAAA,OAAA,EAAA,GACA,EAAA,GACA,EAAA,SACA,EAAA,MASA,IALA,GACA,IAEA,EAAA,EAAA,SAEA,IAAA,EAEA,MADA,GAAA,UACA,EAAA,OAGA,IAAA,GAAA,SAAA,GACA,OAAA,IACA,EAAA,EAGA,IAAA,GAAA,EAAA,EACA,EAAA,GAAA,EAAA,EAAA,EAAA,EAAA,EAEA,GAAA,SACA,EAAA,KAAA,KAAA,EAAA,GACA,EAAA,KAAA,KAAA,EAAA,IAEA,EAAA,EACA,EAAA,EAAA,IAEA,EAAA,OAAA,EAAA,GACA,EAAA,KACA,EAAA,WAUA,OALA,GAAA,SAAA,EAAA,GAEA,EAAA,KAAA,EAAA,GAEA,EAAA,EAAA,GACA,EAAA,SAGA,EAAA,gBAAA,SAAA,EAAA,EAAA,EAAA,GACA,GAAA,GAAA,EAAA,QACA,QAAA,SAAA,IAAA,MAAA,MACA,EAAA,EAEA,IAAA,GAAA,KAAA,YAAA,EAAA,GAAA,wBAAA,IAAA,CAIA,OAHA,GAAA,KACA,GAAA,EAAA,wBAAA,KAEA,KAAA,SAAA,EAAA,EAAA,EAAA,GAGA,IAAA,IACA,WAAA,SAAA,EAAA,EAAA,GACA,GAAA,QAAA,SAAA,GACA,MAAA,MAAA,SAAA,EAAA,KAAA,YAAA,EAAA,EAEA,IAAA,GAAA,EAAA,KACA,OAAA,GAAA,GACA,EAAA,SAAA,SAAA,gBAAA,YAAA,SAAA,KAAA,WAEA,EAAA,YAEA,UAAA,SAAA,EAAA,EAAA,GACA,GAAA,QAAA,SAAA,GACA,MAAA,MAAA,SAAA,KAAA,YAAA,EAAA,EAAA,EAEA,IAAA,GAAA,EAAA,KACA,OAAA,GAAA,GACA,EAAA,SAAA,SAAA,gBAAA,WAAA,SAAA,KAAA,UAEA,EAAA,WAIA,GAAA,wBAAA,SAAA,EAAA,EAAA,EAAA,GACA,MAAA,MAAA,gBAAA,EAAA,EAAA,GAAA,EAAA,IAGA,EAAA,kBAAA,SAAA,EAAA,EAAA,GACA,MAAA,MAAA,UAAA,EAAA,GAAA,EAAA,IAGA,EAAA,mBAAA,SAAA,EAAA,EAAA,GACA,MAAA,MAAA,WAAA,EAAA,GAAA,EAAA,GAIA,IAAA,GAAA,SAAA,EAAA,GACA,MAAA,UAAA,EAAA,GACA,MAAA,GACA,EAAA,MAAA,KAAA,WAEA,EAAA,MAAA,KAAA,YAIA,KAAA,GAAA,KAAA,GACA,EAAA,GAAA,EAAA,GAAA,EAAA,EAAA,GAAA,EAAA,IAAA,EAAA,MC5JA,QAAA,OAAA,wBACA,QAAA,YAAA,UAAA,SAAA,GACA,YAEA,IAAA,IAAA,SAAA,MAAA,IAAA,KAEA,OAAA,UAAA,EAAA,GACA,GAAA,EAAA,GACA,MAAA,GAAA,EAGA,KAAA,GAAA,GADA,EAAA,EAAA,OAAA,EAAA,GAAA,cAAA,EAAA,OAAA,GACA,EAAA,EAAA,EAAA,EAAA,OAAA,IAEA,GADA,EAAA,EAAA,GAAA,EACA,EAAA,GACA,MAAA,GAAA,EAGA,OAAA,OAIA,QAAA,OAAA,6BAAA,sBACA,QAAA,oBAAA,WAAA,WAAA,SAAA,EAAA,GACA,YAEA,IAAA,GAAA,EACA,EAAA,SAAA,GACA,GAAA,IAAA,GAAA,OAAA,UACA,EAAA,KAAA,IAAA,EAAA,IAAA,EAAA,IACA,EAAA,EAAA,WAAA,EAAA,EAAA,IACA,EAEA,OADA,GAAA,EAAA,EACA,EAGA,OAAA,GAAA,wBAAA,MAEA,QAAA,mBAAA,WAAA,WAAA,SAAA,EAAA,GACA,YAEA,IAAA,GAAA,SAAA,GACA,EAAA,OAAA,GAGA,OAAA,GAAA,uBAAA,MC7CA,QAAA,OAAA,mBAAA,gCACA,QAAA,UAAA,aAAA,WAAA,qBAAA,iBAAA,kBAAA,SAAA,EAAA,EAAA,EAAA,EAAA,GACA,YAEA,IAAA,GAAA,SAAA,GACA,GAAA,IAAA,EAAA,GAAA,EACA,EAAA,WACA,GAAA,CACA,IAAA,GAAA,EAAA,UACA,EAAA,EAAA,GACA,EAAA,GAEA,mBAAA,cAAA,YAAA,cAAA,EAAA,UAAA,EAAA,WAAA,EAAA,gBACA,EAAA,EAAA,wBAAA,IAGA,IAAA,GAAA,EAAA,EAAA,EAAA,EAAA,CAKA,KAJA,EAAA,EAAA,MACA,EAAA,EAAA,gBACA,EAAA,OAEA,EAAA,EAAA,EAAA,EAAA,OAAA,IACA,EAAA,EAAA,GACA,EAAA,EAAA,oBACA,GAEA,EAAA,IAAA,EAAA,OAAA,EAAA,IAAA,GAAA,EAAA,IAAA,EAAA,EAAA,UACA,GAAA,EAAA,IAAA,EAAA,OACA,GACA,IAAA,EAAA,IACA,IAAA,GAKA,KACA,EAAA,EAAA,KAEA,IAAA,GAAA,IAAA,IACA,IACA,EAAA,SAAA,YAAA,UACA,EAAA,WAAA,6BAAA,EAAA,WAEA,IACA,EAAA,SAAA,SAAA,UACA,EAAA,WAAA,2BAAA,EAAA,WAEA,EAAA,gBAAA,GAGA,OAAA,GAKA,WACA,EASA,GAAA,GARA,IACA,EAAA,EAAA,WACA,GAAA,EACA,GACA,KAEA,GAAA,KAZA,GAmBA,KAEA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,IACA,GACA,SAUA,OAPA,GAAA,QAAA,EAAA,GACA,EAAA,GAAA,EAEA,EAAA,IAAA,WAAA,WACA,EAAA,KAGA,GAGA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,IACA,EAAA,EAAA,GAAA,EAAA,EAAA,SACA,IACA,EAAA,IAAA,SAAA,EAAA,eAEA,GAAA,IAGA,EAAA,EAAA,GAEA,EAAA,SAAA,GACA,MAAA,GAAA,EAAA,KACA,EAAA,EAAA,KAEA,EAAA,QACA,EAAA,EAAA,SAEA,EAAA,IAGA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,EAAA,EAAA,SAAA,OACA,IAAA,EACA,MAAA,GAAA,EAGA,KAAA,IAAA,GAEA,GADA,EAAA,EAAA,GACA,KAAA,EAAA,MAAA,QAAA,GACA,MAAA,IAKA,EAAA,SAAA,GACA,KAAA,EAAA,YAEA,GADA,EAAA,EAAA,WACA,IAAA,SACA,OAAA,CAGA,QAAA,GAGA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,EACA,KACA,EAAA,MAAA,KAAA,GACA,EAAA,WAAA,EAAA,EAAA,aACA,EAAA,WACA,EAAA,UAAA,IAAA,SAAA,EAAA,SAEA,EAAA,UAAA,EAAA,aAAA,EAAA,SAAA,SACA,EAAA,UAAA,GAAA,SAAA,EAAA,SAAA,eAAA,aAIA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,EACA,KAAA,EAAA,kBACA,EAAA,gBAAA,KAEA,IAAA,GAAA,EAAA,MAAA,QAAA,EACA,MAAA,GACA,EAAA,MAAA,OAAA,EAAA,GAIA,QACA,OAAA,EACA,UAAA,EACA,cAAA,EACA,eAAA,EACA,mBAAA,MClKA,QAAA,OAAA,kCACA,QAAA,sBAAA,YAAA,SAAA,GACA,YAEA,IAAA,MAEA,EAAA,SAAA,EAAA,GACA,GAAA,GAAA,EAAA,GAEA,OADA,GAAA,GAAA,EACA,GAGA,EAAA,SAAA,GACA,MAAA,GAAA,EAAA,KACA,EAAA,IAEA,EAAA,QACA,EAAA,EAAA,SADA,QAMA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,EACA,OAAA,GAAA,EAAA,GAAA,GAGA,EAAA,SAAA,GACA,GAAA,GAAA,EAAA,EACA,UACA,GAAA,GAIA,QACA,eAAA,EACA,aAAA,EACA,aAAA,EACA,gBAAA,MCtCA,QAAA,OAAA,yBAAA,yBAAA,gCACA,UAAA,kBAAA,mBAAA,iBAAA,qBAAA,SAAA,EAAA,EAAA,GACA,YAEA,QACA,KAAA,SAAA,EAAA,EAAA,GACA,EAAA,GAAA,QAAA,SAAA,GACA,GAAA,EAAA,MAAA,KAAA,EAAA,KAAA,QAAA,KAAA,CAEA,GAAA,GAAA,SAAA,eAAA,EAAA,KAAA,QAAA,iBAAA,IAAA,UAAA,GACA,IAAA,GAAA,EAAA,sBAAA,CAEA,EAAA,iBAAA,EAAA,kBACA,EAAA,gBAAA,EAAA,gBAEA,IAAA,GAAA,EAAA,OAAA,SAAA,EAAA,OAAA,IAAA,EACA,EAAA,EAAA,SAAA,SAAA,EAAA,SAAA,IAAA,EACA,EAAA,EAAA,aAAA,EAEA,GAAA,gBACA,QAAA,QAAA,GACA,MAAA,GAAA,EAAA,EACA,MAAA,GAAA,EAAA,YCtBA,QAAA,OAAA,uBAAA,oBACA,UAAA,gBAAA,SAAA,SAAA,GACA,YAEA,QACA,SAAA,IACA,OAAA,EACA,QAAA,WACA,OACA,IAAA,SAAA,GACA,EAAA,cAAA,UCVA,QAAA,OAAA,4BAAA,gCACA,UAAA,qBAAA,qBAAA,SAAA,GACA,YAEA,QACA,SAAA,IACA,OAAA,EACA,QAAA,WACA,OACA,IAAA,SAAA,EAAA,EAAA,GACA,EAAA,SAAA,oBAAA,SAAA,GACA,QAAA,SAAA,KACA,EAAA,SAAA,eAAA,IAGA,EAAA,QAAA,UAAA,GAAA,QAAA,QAAA,GAAA,EACA,EAAA,aAAA,EAAA,GACA,EAAA,IAAA,WAAA,WACA,EAAA,gBAAA,cClBA,QAAA,OAAA,sBAAA,oBACA,UAAA,eAAA,SAAA,iBAAA,WAAA,aAAA,SAAA,EAAA,EAAA,EAAA,GACA,YAEA,IAAA,GAAA,SAAA,EAAA,EAAA,GACA,QAAA,UAAA,GACA,KAAA,OAAA,EACA,QAAA,SAAA,KACA,KAAA,SAAA,GAEA,KAAA,SAAA,EACA,KAAA,OAAA,EAuBA,OApBA,GAAA,UAAA,iBAAA,WAIA,OAHA,KAAA,QAAA,KAAA,WACA,KAAA,OAAA,SAAA,eAAA,KAAA,WAEA,KAAA,QAGA,EAAA,UAAA,kBAAA,WACA,GAAA,GAAA,KAAA,kBACA,OAAA,GACA,EAAA,wBADA,QAKA,EAAA,UAAA,iBAAA,WACA,KAAA,WACA,KAAA,OAAA,UAKA,KAAA,SAAA,EAAA,EAAA,GACA,GACA,GADA,EAAA,EAAA,QAAA,EAAA,IAGA,IAAA,KAAA,EAAA,QAAA,KACA,EAAA,EAAA,QAAA,iBAAA,IAAA,UAAA,GACA,EAAA,cACA,EAAA,EAAA,aAEA,GAIA,EAAA,WACA,GAAA,GAAA,GAAA,GAAA,EAAA,IAAA,EAAA,OAAA,SAAA,EAAA,OAAA,IAAA,GACA,GAAA,OAAA,GAEA,EAAA,IAAA,WAAA,WACA,EAAA,UAAA,KAEA,EAAA,IAAA,yBAAA,EAAA,iBAAA,KAAA,IACA,EAAA,IAAA,sBAAA,EAAA,iBAAA,KAAA,KACA,GAAA","sourcesContent":["/**\n * x is a value between 0 and 1, indicating where in the animation you are.\n */\nvar duScrollDefaultEasing = function (x) {\n 'use strict';\n\n if(x < 0.5) {\n return Math.pow(x*2, 2)/2;\n }\n return 1-Math.pow((1-x)*2, 2)/2;\n};\n\nangular.module('duScroll', [\n 'duScroll.scrollspy', \n 'duScroll.smoothScroll', \n 'duScroll.scrollContainer', \n 'duScroll.spyContext',\n 'duScroll.scrollHelpers'\n])\n //Default animation duration for smoothScroll directive\n .value('duScrollDuration', 350)\n //Scrollspy debounce interval, set to 0 to disable\n .value('duScrollSpyWait', 100)\n //Wether or not multiple scrollspies can be active at once \n .value('duScrollGreedy', false)\n //Default offset for smoothScroll directive\n .value('duScrollOffset', 0)\n //Default easing function for scroll animation\n .value('duScrollEasing', duScrollDefaultEasing);\n","angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation'])\n.run(function($window, $q, cancelAnimation, requestAnimation, duScrollEasing, duScrollDuration, duScrollOffset) {\n 'use strict';\n\n var proto = angular.element.prototype;\n\n var isDocument = function(el) {\n return (typeof HTMLDocument !== 'undefined' && el instanceof HTMLDocument) || (el.nodeType && el.nodeType === el.DOCUMENT_NODE);\n };\n\n var isElement = function(el) {\n return (typeof HTMLElement !== 'undefined' && el instanceof HTMLElement) || (el.nodeType && el.nodeType === el.ELEMENT_NODE);\n };\n\n var unwrap = function(el) {\n return isElement(el) || isDocument(el) ? el : el[0];\n };\n\n proto.scrollTo = function(left, top, duration, easing) {\n var aliasFn;\n if(angular.isElement(left)) {\n aliasFn = this.scrollToElement;\n } else if(duration) {\n aliasFn = this.scrollToAnimated;\n }\n if(aliasFn) {\n return aliasFn.apply(this, arguments);\n }\n var el = unwrap(this);\n if(isDocument(el)) {\n return $window.scrollTo(left, top);\n }\n el.scrollLeft = left;\n el.scrollTop = top;\n };\n\n var scrollAnimation, deferred;\n proto.scrollToAnimated = function(left, top, duration, easing) {\n if(duration && !easing) {\n easing = duScrollEasing;\n }\n var startLeft = this.scrollLeft(),\n startTop = this.scrollTop(),\n deltaLeft = Math.round(left - startLeft),\n deltaTop = Math.round(top - startTop);\n\n var startTime = null;\n var el = this;\n\n var cancelOnEvents = 'scroll mousedown mousewheel touchmove keydown';\n var cancelScrollAnimation = function($event) {\n if (!$event || $event.which > 0) {\n el.unbind(cancelOnEvents, cancelScrollAnimation);\n cancelAnimation(scrollAnimation);\n deferred.reject();\n scrollAnimation = null;\n }\n };\n\n if(scrollAnimation) {\n cancelScrollAnimation();\n }\n deferred = $q.defer();\n\n if(!deltaLeft && !deltaTop) {\n deferred.resolve();\n return deferred.promise;\n }\n\n var animationStep = function(timestamp) {\n if (startTime === null) {\n startTime = timestamp;\n }\n\n var progress = timestamp - startTime;\n var percent = (progress >= duration ? 1 : easing(progress/duration));\n\n el.scrollTo(\n startLeft + Math.ceil(deltaLeft * percent),\n startTop + Math.ceil(deltaTop * percent)\n );\n if(percent < 1) {\n scrollAnimation = requestAnimation(animationStep);\n } else {\n el.unbind(cancelOnEvents, cancelScrollAnimation);\n scrollAnimation = null;\n deferred.resolve();\n }\n };\n\n //Fix random mobile safari bug when scrolling to top by hitting status bar\n el.scrollTo(startLeft, startTop);\n\n el.bind(cancelOnEvents, cancelScrollAnimation);\n\n scrollAnimation = requestAnimation(animationStep);\n return deferred.promise;\n };\n\n proto.scrollToElement = function(target, offset, duration, easing) {\n var el = unwrap(this);\n if(!angular.isNumber(offset) || isNaN(offset)) {\n offset = duScrollOffset;\n }\n var top = this.scrollTop() + unwrap(target).getBoundingClientRect().top - offset;\n if(isElement(el)) {\n top -= el.getBoundingClientRect().top;\n }\n return this.scrollTo(0, top, duration, easing);\n };\n\n var overloaders = {\n scrollLeft: function(value, duration, easing) {\n if(angular.isNumber(value)) {\n return this.scrollTo(value, this.scrollTop(), duration, easing);\n }\n var el = unwrap(this);\n if(isDocument(el)) {\n return $window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft;\n }\n return el.scrollLeft;\n },\n scrollTop: function(value, duration, easing) {\n if(angular.isNumber(value)) {\n return this.scrollTo(this.scrollTop(), value, duration, easing);\n }\n var el = unwrap(this);\n if(isDocument(el)) {\n return $window.scrollY || document.documentElement.scrollTop || document.body.scrollTop;\n }\n return el.scrollTop;\n }\n };\n\n proto.scrollToElementAnimated = function(target, offset, duration, easing) {\n return this.scrollToElement(target, offset, duration || duScrollDuration, easing);\n };\n\n proto.scrollTopAnimated = function(top, duration, easing) {\n return this.scrollTop(top, duration || duScrollDuration, easing);\n };\n\n proto.scrollLeftAnimated = function(left, duration, easing) {\n return this.scrollLeft(left, duration || duScrollDuration, easing);\n };\n\n //Add duration and easing functionality to existing jQuery getter/setters\n var overloadScrollPos = function(superFn, overloadFn) {\n return function(value, duration, easing) {\n if(duration) {\n return overloadFn.apply(this, arguments);\n }\n return superFn.apply(this, arguments);\n };\n };\n\n for(var methodName in overloaders) {\n proto[methodName] = (proto[methodName] ? overloadScrollPos(proto[methodName], overloaders[methodName]) : overloaders[methodName]);\n }\n});\n","//Adapted from https://gist.github.com/paulirish/1579671\nangular.module('duScroll.polyfill', [])\n.factory('polyfill', function($window) {\n 'use strict';\n\n var vendors = ['webkit', 'moz', 'o', 'ms'];\n\n return function(fnName, fallback) {\n if($window[fnName]) {\n return $window[fnName];\n }\n var suffix = fnName.substr(0, 1).toUpperCase() + fnName.substr(1);\n for(var key, i = 0; i < vendors.length; i++) {\n key = vendors[i]+suffix;\n if($window[key]) {\n return $window[key];\n }\n }\n return fallback;\n };\n});\n\nangular.module('duScroll.requestAnimation', ['duScroll.polyfill'])\n.factory('requestAnimation', function(polyfill, $timeout) {\n 'use strict';\n\n var lastTime = 0;\n var fallback = function(callback, element) {\n var currTime = new Date().getTime();\n var timeToCall = Math.max(0, 16 - (currTime - lastTime));\n var id = $timeout(function() { callback(currTime + timeToCall); },\n timeToCall);\n lastTime = currTime + timeToCall;\n return id;\n };\n \n return polyfill('requestAnimationFrame', fallback);\n})\n.factory('cancelAnimation', function(polyfill, $timeout) {\n 'use strict';\n\n var fallback = function(promise) {\n $timeout.cancel(promise);\n };\n\n return polyfill('cancelAnimationFrame', fallback);\n});\n","angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])\n.factory('spyAPI', function($rootScope, $timeout, scrollContainerAPI, duScrollGreedy, duScrollSpyWait) {\n 'use strict';\n\n var createScrollHandler = function(context) {\n var timer = false, queued = false;\n var handler = function() {\n queued = false;\n var container = context.container,\n containerEl = container[0],\n containerOffset = 0;\n\n if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {\n containerOffset = containerEl.getBoundingClientRect().top;\n }\n\n var i, currentlyActive, toBeActive, spies, spy, pos;\n spies = context.spies;\n currentlyActive = context.currentlyActive;\n toBeActive = undefined;\n\n for(i = 0; i < spies.length; i++) {\n spy = spies[i];\n pos = spy.getTargetPosition();\n if (!pos) continue;\n\n if(pos.top + spy.offset - containerOffset < 20 && (pos.top*-1 + containerOffset) < pos.height) {\n if(!toBeActive || toBeActive.top < pos.top) {\n toBeActive = {\n top: pos.top,\n spy: spy\n };\n }\n }\n }\n if(toBeActive) {\n toBeActive = toBeActive.spy;\n }\n if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;\n if(currentlyActive) {\n currentlyActive.$element.removeClass('active');\n $rootScope.$broadcast('duScrollspy:becameInactive', currentlyActive.$element);\n }\n if(toBeActive) {\n toBeActive.$element.addClass('active');\n $rootScope.$broadcast('duScrollspy:becameActive', toBeActive.$element);\n }\n context.currentlyActive = toBeActive;\n };\n\n if(!duScrollSpyWait) {\n return handler;\n }\n\n //Debounce for potential performance savings\n return function() {\n if(!timer) {\n handler();\n timer = $timeout(function() {\n timer = false;\n if(queued) {\n handler();\n }\n }, duScrollSpyWait, false);\n } else {\n queued = true;\n }\n };\n };\n\n var contexts = {};\n\n var createContext = function($scope) {\n var id = $scope.$id;\n var context = {\n spies: []\n };\n\n context.handler = createScrollHandler(context);\n contexts[id] = context;\n\n $scope.$on('$destroy', function() {\n destroyContext($scope);\n });\n\n return id;\n };\n\n var destroyContext = function($scope) {\n var id = $scope.$id;\n var context = contexts[id], container = context.container;\n if(container) {\n container.off('scroll', context.handler);\n }\n delete contexts[id];\n };\n\n var defaultContextId = createContext($rootScope);\n\n var getContextForScope = function(scope) {\n if(contexts[scope.$id]) {\n return contexts[scope.$id];\n }\n if(scope.$parent) {\n return getContextForScope(scope.$parent);\n }\n return contexts[defaultContextId];\n };\n\n var getContextForSpy = function(spy) {\n var context, contextId, scope = spy.$element.scope();\n if(scope) {\n return getContextForScope(scope);\n }\n //No scope, most likely destroyed\n for(contextId in contexts) {\n context = contexts[contextId];\n if(context.spies.indexOf(spy) !== -1) {\n return context;\n }\n }\n };\n\n var isElementInDocument = function(element) {\n while (element.parentNode) {\n element = element.parentNode;\n if (element === document) {\n return true;\n }\n }\n return false;\n };\n\n var addSpy = function(spy) {\n var context = getContextForSpy(spy);\n if (!context) return;\n context.spies.push(spy);\n if (!context.container || !isElementInDocument(context.container)) {\n if(context.container) {\n context.container.off('scroll', context.handler);\n }\n context.container = scrollContainerAPI.getContainer(spy.$element.scope());\n context.container.on('scroll', context.handler).triggerHandler('scroll');\n }\n };\n\n var removeSpy = function(spy) {\n var context = getContextForSpy(spy);\n if(spy === context.currentlyActive) {\n context.currentlyActive = null;\n }\n var i = context.spies.indexOf(spy);\n if(i !== -1) {\n context.spies.splice(i, 1);\n }\n };\n\n return {\n addSpy: addSpy,\n removeSpy: removeSpy,\n createContext: createContext,\n destroyContext: destroyContext,\n getContextForScope: getContextForScope\n };\n});\n","angular.module('duScroll.scrollContainerAPI', [])\n.factory('scrollContainerAPI', function($document) {\n 'use strict';\n\n var containers = {};\n\n var setContainer = function(scope, element) {\n var id = scope.$id;\n containers[id] = element;\n return id;\n };\n\n var getContainerId = function(scope) {\n if(containers[scope.$id]) {\n return scope.$id;\n }\n if(scope.$parent) {\n return getContainerId(scope.$parent);\n }\n return;\n };\n\n var getContainer = function(scope) {\n var id = getContainerId(scope);\n return id ? containers[id] : $document;\n };\n\n var removeContainer = function(scope) {\n var id = getContainerId(scope);\n if(id) {\n delete containers[id];\n }\n };\n\n return {\n getContainerId: getContainerId, \n getContainer: getContainer, \n setContainer: setContainer,\n removeContainer: removeContainer\n };\n});\n","angular.module('duScroll.smoothScroll', ['duScroll.scrollHelpers', 'duScroll.scrollContainerAPI'])\n.directive('duSmoothScroll', function(duScrollDuration, duScrollOffset, scrollContainerAPI) {\n 'use strict';\n\n return {\n link : function($scope, $element, $attr) {\n $element.on('click', function(e) {\n if(!$attr.href || $attr.href.indexOf('#') === -1) return;\n\n var target = document.getElementById($attr.href.replace(/.*(?=#[^\\s]+$)/, '').substring(1));\n if(!target || !target.getBoundingClientRect) return;\n \n if (e.stopPropagation) e.stopPropagation();\n if (e.preventDefault) e.preventDefault();\n\n var offset = $attr.offset ? parseInt($attr.offset, 10) : duScrollOffset;\n var duration = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration;\n var container = scrollContainerAPI.getContainer($scope);\n\n container.scrollToElement(\n angular.element(target), \n isNaN(offset) ? 0 : offset, \n isNaN(duration) ? 0 : duration\n );\n });\n }\n };\n});\n","angular.module('duScroll.spyContext', ['duScroll.spyAPI'])\n.directive('duSpyContext', function(spyAPI) {\n 'use strict';\n\n return {\n restrict: 'A',\n scope: true,\n compile: function compile(tElement, tAttrs, transclude) {\n return {\n pre: function preLink($scope, iElement, iAttrs, controller) {\n spyAPI.createContext($scope);\n }\n };\n }\n };\n});\n","angular.module('duScroll.scrollContainer', ['duScroll.scrollContainerAPI'])\n.directive('duScrollContainer', function(scrollContainerAPI){\n 'use strict';\n\n return {\n restrict: 'A',\n scope: true,\n compile: function compile(tElement, tAttrs, transclude) {\n return {\n pre: function preLink($scope, iElement, iAttrs, controller) {\n iAttrs.$observe('duScrollContainer', function(element) {\n if(angular.isString(element)) {\n element = document.getElementById(element);\n }\n\n element = (angular.isElement(element) ? angular.element(element) : iElement);\n scrollContainerAPI.setContainer($scope, element);\n $scope.$on('$destroy', function() {\n scrollContainerAPI.removeContainer($scope);\n });\n });\n }\n };\n }\n };\n});\n","angular.module('duScroll.scrollspy', ['duScroll.spyAPI'])\n.directive('duScrollspy', function(spyAPI, duScrollOffset, $timeout, $rootScope) {\n 'use strict';\n\n var Spy = function(targetElementOrId, $element, offset) {\n if(angular.isElement(targetElementOrId)) {\n this.target = targetElementOrId;\n } else if(angular.isString(targetElementOrId)) {\n this.targetId = targetElementOrId;\n }\n this.$element = $element;\n this.offset = offset;\n };\n\n Spy.prototype.getTargetElement = function() {\n if (!this.target && this.targetId) {\n this.target = document.getElementById(this.targetId);\n }\n return this.target;\n };\n\n Spy.prototype.getTargetPosition = function() {\n var target = this.getTargetElement();\n if(target) {\n return target.getBoundingClientRect();\n }\n };\n\n Spy.prototype.flushTargetCache = function() {\n if(this.targetId) {\n this.target = undefined;\n }\n };\n\n return {\n link: function ($scope, $element, $attr) {\n var href = $attr.ngHref || $attr.href;\n var targetId;\n\n if (href && href.indexOf('#') !== -1) {\n targetId = href.replace(/.*(?=#[^\\s]+$)/, '').substring(1);\n } else if($attr.duScrollspy) {\n targetId = $attr.duScrollspy;\n }\n if(!targetId) return;\n\n // Run this in the next execution loop so that the scroll context has a chance\n // to initialize\n $timeout(function() {\n var spy = new Spy(targetId, $element, -($attr.offset ? parseInt($attr.offset, 10) : duScrollOffset));\n spyAPI.addSpy(spy);\n\n $scope.$on('$destroy', function() {\n spyAPI.removeSpy(spy);\n });\n $scope.$on('$locationChangeSuccess', spy.flushTargetCache.bind(spy));\n $rootScope.$on('$stateChangeSuccess', spy.flushTargetCache.bind(spy));\n }, 0, false);\n }\n };\n});\n"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/bower.json b/bower.json index bb2b011..e825d46 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-scroll", - "version": "0.6.1", + "version": "0.6.2", "main": "angular-scroll.min.js", "ignore": [ "**/.*", diff --git a/package.json b/package.json index 13e33b9..ef62662 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-scroll", - "version": "0.6.1", + "version": "0.6.2", "description": "Scrollspy, animated scrollTo and scroll events", "keywords": [ "angular",