From bc77e4726f917c9f75227ea2958ad52ce96bf73d Mon Sep 17 00:00:00 2001 From: Martin Gruber Date: Tue, 10 Feb 2015 09:52:49 +0100 Subject: [PATCH 1/4] Added: refresh() function now accepts optional 'data' parameter which can affect the way the items are scrolled in the view. --- src/angular-vs-repeat.js | 327 +++++++++++++++++++++++++++++------ src/angular-vs-repeat.min.js | 2 +- 2 files changed, 273 insertions(+), 56 deletions(-) diff --git a/src/angular-vs-repeat.js b/src/angular-vs-repeat.js index 11e34d7..0f8e188 100644 --- a/src/angular-vs-repeat.js +++ b/src/angular-vs-repeat.js @@ -58,7 +58,7 @@ // - 'vsRepeatTrigger' - an event the directive listens for to manually trigger reinitialization // - 'vsRepeatReinitialized' - an event the directive emits upon reinitialization done - var isMacOS = navigator.appVersion.indexOf('Mac') != -1, + var isMacOS = navigator.appVersion.indexOf('Mac') !== -1, wheelEventName = typeof window.onwheel !== 'undefined' ? 'wheel' : typeof window.onmousewheel !== 'undefined' ? 'mousewheel' : 'DOMMouseScroll', dde = document.documentElement, matchingFunction = dde.matches ? 'matches' : @@ -76,10 +76,12 @@ el = el.parentNode; } - if(el && el[matchingFunction](selector)) + if(el && el[matchingFunction](selector)) { return angular.element(el); - else + } + else { return angular.element(); + } }; angular.module('vs-repeat', []).directive('vsRepeat', ['$compile', function($compile){ @@ -108,14 +110,15 @@ }; $element.empty(); - if(!window.getComputedStyle || window.getComputedStyle($element[0]).position !== 'absolute') + if(!window.getComputedStyle || window.getComputedStyle($element[0]).position !== 'absolute') { $element.css('position', 'relative'); + } return { pre: function($scope, $element, $attrs, $ctrl){ var childClone = angular.element(childCloneHtml), originalCollection = [], originalLength, - $$horizontal = typeof $attrs.vsHorizontal !== "undefined", + $$horizontal = typeof $attrs.vsHorizontal !== 'undefined', $wheelHelper, $fillElement, autoSize = !$attrs.vsRepeat, @@ -128,10 +131,10 @@ offsetSize = $$horizontal ? 'offsetWidth' : 'offsetHeight', scrollPos = $$horizontal ? 'scrollLeft' : 'scrollTop'; - if($scrollParent.length === 0) throw 'Specified scroll parent selector did not match any element'; + if($scrollParent.length === 0) { throw 'Specified scroll parent selector did not match any element'; } $scope.$scrollParent = $scrollParent; - if(sizesPropertyExists) $scope.sizesCumulative = []; + if(sizesPropertyExists) { $scope.sizesCumulative = []; } //initial defaults $scope.elementSize = $scrollParent[0][clientSize] || 50; @@ -154,7 +157,7 @@ refresh(); }); - function refresh(){ + function refresh(event, data){ if(!originalCollection || originalCollection.length < 1){ $scope[collectionName] = []; originalLength = 0; @@ -179,7 +182,7 @@ setAutoSize(); } - reinitialize(); + reinitialize(data); } function setAutoSize(){ @@ -194,8 +197,9 @@ $scope.elementSize = children[i][offsetSize]; reinitialize(); autoSize = false; - if($scope.$root && !$scope.$root.$$phase) + if($scope.$root && !$scope.$root.$$phase) { $scope.$apply(); + } } break; } @@ -221,10 +225,10 @@ '(sizesCumulative[$index + startIndex] + offsetBefore)' : '(($index + startIndex) * elementSize + offsetBefore)'; - if(typeof document.documentElement.style.transform !== "undefined"){ // browser supports transform css property + if(typeof document.documentElement.style.transform !== 'undefined'){ // browser supports transform css property childClone.attr('ng-style', '{ "transform": "' + positioningPropertyTransform + '(" + ' + offsetCalculationString + ' + "px)"}'); } - else if(typeof document.documentElement.style.webkitTransform !== "undefined"){ // browser supports -webkit-transform css property + else if(typeof document.documentElement.style.webkitTransform !== 'undefined'){ // browser supports -webkit-transform css property childClone.attr('ng-style', '{ "-webkit-transform": "' + positioningPropertyTransform + '(" + ' + offsetCalculationString + ' + "px)"}'); } else{ @@ -245,17 +249,18 @@ $scope.$fillElement = $fillElement; var _prevMouse = {}; - if(isMacOS){ + if(isMacOS) { $wheelHelper = angular.element('
') .on(wheelEventName, function(e){ e.preventDefault(); e.stopPropagation(); - if(e.originalEvent) e = e.originalEvent; + if(e.originalEvent) { e = e.originalEvent; } $scrollParent[0].scrollLeft += (e.deltaX || -e.wheelDeltaX); $scrollParent[0].scrollTop += (e.deltaY || -e.wheelDeltaY); }).on('mousemove', function(e){ - if(_prevMouse.x !== e.clientX || _prevMouse.y !== e.clientY) + if(_prevMouse.x !== e.clientX || _prevMouse.y !== e.clientY) { angular.element(this).css('display', 'none'); + } _prevMouse = { x: e.clientX, y: e.clientY @@ -268,28 +273,33 @@ $scope.endIndex = 0; $scrollParent.on('scroll', function scrollHandler(e){ - if(updateInnerCollection()) + // console.log('Parent scrolling: ', $scrollParent[0][scrollPos]); + if(updateInnerCollection()) { $scope.$apply(); + } }); - if(isMacOS){ - $scrollParent.on(wheelEventName, wheelHandler); - } - function wheelHandler(e){ + function wheelHandler(e) { var elem = e.currentTarget; - if(elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight) + if(elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight) { $wheelHelper.css('display', 'block'); + } + } + if(isMacOS){ + $scrollParent.on(wheelEventName, wheelHandler); } function onWindowResize(){ if(typeof $attrs.vsAutoresize !== 'undefined'){ autoSize = true; setAutoSize(); - if($scope.$root && !$scope.$root.$$phase) + if($scope.$root && !$scope.$root.$$phase) { $scope.$apply(); + } } - if(updateInnerCollection()) + if(updateInnerCollection()) { $scope.$apply(); + } } angular.element(window).on('resize', onWindowResize); @@ -305,15 +315,16 @@ var _prevStartIndex, _prevEndIndex; - function reinitialize(){ + function reinitialize(data){ _prevStartIndex = void 0; _prevEndIndex = void 0; - updateInnerCollection(); + updateInnerCollection(data); resizeFillElement(sizesPropertyExists ? $scope.sizesCumulative[originalLength] : $scope.elementSize*originalLength ); - $scope.$emit('vsRepeatReinitialized'); + // console.log('Emitting the vsRepeatReinitialized event, $scope.startIndex: ', $scope.startIndex, ', $scope.endIndex: ', $scope.endIndex); + $scope.$emit('vsRepeatReinitialized', $scope.startIndex, $scope.endIndex); } function resizeFillElement(size){ @@ -324,10 +335,11 @@ }); if($ctrl && $ctrl.$fillElement){ var referenceElement = $ctrl.$fillElement[0].parentNode.querySelector('[ng-repeat]'); - if(referenceElement) + if(referenceElement) { $ctrl.$fillElement.css({ 'width': referenceElement.scrollWidth + 'px' }); + } } } else{ @@ -337,10 +349,11 @@ }); if($ctrl && $ctrl.$fillElement){ referenceElement = $ctrl.$fillElement[0].parentNode.querySelector('[ng-repeat]'); - if(referenceElement) + if(referenceElement) { $ctrl.$fillElement.css({ 'height': referenceElement.scrollHeight + 'px' }); + } } } } @@ -350,52 +363,256 @@ var ch = $scrollParent[0][clientSize]; if(ch !== _prevClientSize){ reinitialize(); - if($scope.$root && !$scope.$root.$$phase) + if($scope.$root && !$scope.$root.$$phase) { $scope.$apply(); + } } _prevClientSize = ch; } $scope.$watch(function(){ - if(typeof window.requestAnimationFrame === "function") + if(typeof window.requestAnimationFrame === 'function') { window.requestAnimationFrame(reinitOnClientHeightChange); - else + } + else { reinitOnClientHeightChange(); + } }); - function updateInnerCollection(){ - if(sizesPropertyExists){ - $scope.startIndex = 0; - while($scope.sizesCumulative[$scope.startIndex] < $scrollParent[0][scrollPos] - $scope.offsetBefore) - $scope.startIndex++; - if($scope.startIndex > 0) $scope.startIndex--; + // Scroll to required position + // scrollTo - number of pixels to be scrolled to + function scrollToPosition(scrollTo) { + var scrolled = false; + if (scrollTo !== undefined && (typeof scrollTo) === 'number') { + // Set the position to be scrolled to + scrolled = Math.max(scrollTo, 0); + // console.log('Scrolling to ' + scrolled + 'px'); + + // Is there a scroll change? + if ($scrollParent[0][scrollPos] !== scrolled) { + $scrollParent[0][scrollPos] = scrolled; + } + else { + scrolled = false; + } - $scope.endIndex = $scope.startIndex; - while($scope.sizesCumulative[$scope.endIndex] < $scrollParent[0][scrollPos] - $scope.offsetBefore + $scrollParent[0][clientSize]) - $scope.endIndex++; + // Emit the event + // console.log('Emitting the vsRepeatScrolled event, scrolled: ', scrolled); + $scope.$emit('vsRepeatScrolled', scrolled); } - else{ - $scope.startIndex = Math.max( - Math.floor( - ($scrollParent[0][scrollPos] - $scope.offsetBefore) / $scope.elementSize + $scope.excess/2 - ) - $scope.excess, - 0 - ); - - $scope.endIndex = Math.min( - $scope.startIndex + Math.ceil( - $scrollParent[0][clientSize] / $scope.elementSize - ) + $scope.excess, - originalLength - ); + return scrolled; + } + + // Update the inner collection, i.e. do the proper slicing + // data - an object with the following properties: + // * startIndex - index of the item that should be considered as the first item; + // it is not necessarily the one that will be rendered at the first position, + // the first rendered item depends also on the startIndexPosition + // of the item at startIndex + // * startIndexPosition - position of the first item, i.e. how much it should + // be scrolled; if not provided, no scrolling is performed + // - either a number of pixels or one of the following strings: + // 'top', 'middle', 'bottom', 'inview' is the same as 'inview#top', + // 'inview#middle', 'inview#bottom', 'inview#auto'; + // the 'inview#' settings means that if the item is + // already in the view, nothing is scrolled, but if it is not, + // then the item will be scrolled accordingly (to be in the ); + // position 'auto' means that it will be either 'top' or 'bottom' + // depending on what is closer to the current item position + function updateInnerCollection(data){ + + var scrollChange = true, + position, + visibleStartIndex; + if (data && data.startIndex !== undefined) { + + // Item scroll position relative to the view, i.e. position === 0 means the top of the view, + // position === $scrollParent[0][clientSize] means the bottom + if (data.startIndexPosition) { + // console.log('Required start index position: ', data.startIndexPosition); + position = 0; + switch (typeof data.startIndexPosition) { + case 'number': + position = data.startIndexPosition + $scope.offsetBefore; + break; + case 'string': + switch (data.startIndexPosition) { + case 'top': + position = $scope.offsetBefore; + break; + case 'middle': + position = ($scrollParent[0][clientSize] - $scope.sizes[data.startIndex]) / 2; + break; + case 'bottom': + position = $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] - $scope.offsetAfter; + break; + case 'inview': + case 'inview#top': + case 'inview#middle': + case 'inview#bottom': + case 'inview#auto': + // The item is in the viewport, do nothing + if ( + ($scrollParent[0][scrollPos] <= ($scope.sizesCumulative[data.startIndex])) && + ($scrollParent[0][scrollPos] + $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] >= $scope.sizesCumulative[data.startIndex])) { + scrollChange = false; + // The current item scroll position + position = $scope.sizesCumulative[data.startIndex] - $scrollParent[0][scrollPos]; + } + // The item is out of the viewport + else { + if (data.startIndexPosition === 'inview#top' || data.startIndexPosition === 'inview') { + // Get it at the top + position = $scope.offsetBefore; + } + if (data.startIndexPosition === 'inview#bottom') { + // Get it at the bottom + position = $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] + $scope.offsetAfter; + } + if (data.startIndexPosition === 'inview#middle') { + // Get it at the middle + position = ($scrollParent[0][clientSize] - $scope.sizes[data.startIndex]) / 2; + } + if (data.startIndexPosition === 'inview#auto') { + // Get it at the bottom or at the top, depending on what is closer + if ($scrollParent[0][scrollPos] <= $scope.sizesCumulative[data.startIndex]) { + position = $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] + $scope.offsetAfter; + } + else { + position = $scope.offsetBefore; + } + } + } + break; + default: + console.warn('Incorrect startIndexPosition string value'); + break; + } + break; + default: + console.warn('Incorrect startIndexPosition type'); + break; + } + } + else { + // The item is not required to be in the viewport, do nothing + scrollChange = false; + // The current item scroll position + position = $scope.sizesCumulative[data.startIndex] - $scrollParent[0][scrollPos]; + } + + // console.log('Required start index: ', data.startIndex); + // console.log('Item position in the view: ', position); + // console.log('Cumulative size for start index: ', $scope.sizesCumulative[data.startIndex]); + + $scope.startIndex = data.startIndex; + + if(sizesPropertyExists){ + + while($scope.sizesCumulative[$scope.startIndex] > $scope.sizesCumulative[data.startIndex] - position) + { + $scope.startIndex--; + } + // The real first item in the view + visibleStartIndex = $scope.startIndex; + // Adjust the start index according to the excess + $scope.startIndex = Math.max( + Math.floor($scope.startIndex - ($scope.excess / 2)), + 0 + ); + + $scope.endIndex = $scope.startIndex; + while($scope.sizesCumulative[$scope.endIndex] < $scope.sizesCumulative[visibleStartIndex] - $scope.offsetBefore + $scrollParent[0][clientSize]) { + $scope.endIndex++; + } + // Adjust the end index according to the excess + $scope.endIndex = Math.min( + Math.ceil($scope.endIndex + ($scope.excess / 2)), + originalLength + ); + + } + else { + + while(($scope.startIndex * $scope.elementSize) > (data.startIndex * $scope.elementSize) - position) + { + $scope.startIndex--; + } + // The real first item in the view + visibleStartIndex = $scope.startIndex; + $scope.startIndex = Math.max( + Math.floor($scope.startIndex - ($scope.excess / 2)), + 0 + ); + + $scope.endIndex = Math.min( + $scope.startIndex + Math.ceil($scrollParent[0][clientSize] / $scope.elementSize) + $scope.excess / 2, + originalLength + ); + + } } + else { + if(sizesPropertyExists){ + $scope.startIndex = 0; + while($scope.sizesCumulative[$scope.startIndex] < $scrollParent[0][scrollPos] - $scope.offsetBefore) { + $scope.startIndex++; + } + if($scope.startIndex > 0) { $scope.startIndex--; } + // Adjust the start index according to the excess + $scope.startIndex = Math.max( + Math.floor($scope.startIndex - $scope.excess / 2), + 0 + ); + + $scope.endIndex = $scope.startIndex; + while($scope.sizesCumulative[$scope.endIndex] < $scrollParent[0][scrollPos] - $scope.offsetBefore + $scrollParent[0][clientSize]) { + $scope.endIndex++; + } + // Adjust the end index according to the excess + $scope.endIndex = Math.min( + Math.ceil($scope.endIndex + $scope.excess / 2), + originalLength + ); + } + else{ + $scope.startIndex = Math.max( + Math.floor( + ($scrollParent[0][scrollPos] - $scope.offsetBefore) / $scope.elementSize + $scope.excess / 2 + ) - $scope.excess, + 0 + ); + + $scope.endIndex = Math.min( + $scope.startIndex + Math.ceil( + $scrollParent[0][clientSize] / $scope.elementSize + ) + $scope.excess, + originalLength + ); + } + } + + var scrolled = false; + if (data !== undefined && data.startIndex !== undefined && position !== undefined && scrollChange) { + // Scroll to the requested position + scrolled = scrollToPosition($scope.sizesCumulative[data.startIndex] - position); + } + + // console.log('Real viewport start index: ', $scope.startIndex); + // console.log('Real viewport end index: ', $scope.endIndex); + // console.log('Scroll position: ', $scrollParent[0][scrollPos]); var digestRequired = $scope.startIndex !== _prevStartIndex || $scope.endIndex !== _prevEndIndex; - if(digestRequired) + if(digestRequired) { $scope[collectionName] = originalCollection.slice($scope.startIndex, $scope.endIndex); + // Emit the event + // console.log('Emitting the vsRepeatInnerCollectionUpdated event, $scope.startIndex: ', $scope.startIndex, ', $scope.endIndex: ', $scope.endIndex, ', _prevStartIndex: ', _prevStartIndex, ', _prevEndIndex: ', _prevEndIndex); + $scope.$emit('vsRepeatInnerCollectionUpdated', $scope.startIndex, $scope.endIndex, _prevStartIndex, _prevEndIndex); + } + _prevStartIndex = $scope.startIndex; _prevEndIndex = $scope.endIndex; diff --git a/src/angular-vs-repeat.min.js b/src/angular-vs-repeat.min.js index b83fbca..881b2f8 100644 --- a/src/angular-vs-repeat.min.js +++ b/src/angular-vs-repeat.min.js @@ -1 +1 @@ -!function(a,b){"use strict";var c=-1!=navigator.appVersion.indexOf("Mac"),d="undefined"!=typeof a.onwheel?"wheel":"undefined"!=typeof a.onmousewheel?"mousewheel":"DOMMouseScroll",e=document.documentElement,f=e.matches?"matches":e.matchesSelector?"matchesSelector":e.webkitMatches?"webkitMatches":e.webkitMatchesSelector?"webkitMatchesSelector":e.msMatches?"msMatches":e.msMatchesSelector?"msMatchesSelector":e.mozMatches?"mozMatches":e.mozMatchesSelector?"mozMatchesSelector":null,g=b.element.prototype.closest||function(a){for(var c=this[0].parentNode;c!==document.documentElement&&null!=c&&!c[f](a);)c=c.parentNode;return c&&c[f](a)?b.element(c):b.element()};b.module("vs-repeat",[]).directive("vsRepeat",["$compile",function(e){return{restrict:"A",scope:!0,require:"?^vsRepeat",controller:["$scope",function(a){this.$scrollParent=a.$scrollParent,this.$fillElement=a.$fillElement}],compile:function(f){var h=f.children().eq(0),i=h.attr("ng-repeat"),j=h[0].outerHTML,k=/^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(i),l=k[1],m=k[2],n=k[3],o="$vs_collection",p={vsRepeat:"elementSize",vsOffsetBefore:"offsetBefore",vsOffsetAfter:"offsetAfter",vsExcess:"excess"};return f.empty(),a.getComputedStyle&&"absolute"===a.getComputedStyle(f[0]).position||f.css("position","relative"),{pre:function(f,h,i,k){function q(){if(!C||C.length<1)return f[o]=[],y=0,v(0),void(f.sizesCumulative=[0]);if(y=C.length,F){f.sizes=C.map(function(a){return a[i.vsSizeProperty]});var a=0;f.sizesCumulative=f.sizes.map(function(b){var c=a;return a+=b,c}),f.sizesCumulative.push(a)}r(),u()}function r(){E&&f.$$postDigest(function(){if(h[0].offsetHeight||h[0].offsetWidth)for(var a=h.children(),b=0;bb.clientWidth||b.scrollHeight>b.clientHeight)&&z.css("display","block")}function t(){"undefined"!=typeof i.vsAutoresize&&(E=!0,r(),f.$root&&!f.$root.$$phase&&f.$apply()),x()&&f.$apply()}function u(){O=void 0,P=void 0,x(),v(F?f.sizesCumulative[y]:f.elementSize*y),f.$emit("vsRepeatReinitialized")}function v(a){if(D){if(A.css({width:f.offsetBefore+a+f.offsetAfter+"px",height:"100%"}),k&&k.$fillElement){var b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]");b&&k.$fillElement.css({width:b.scrollWidth+"px"})}}else A.css({height:f.offsetBefore+a+f.offsetAfter+"px",width:"100%"}),k&&k.$fillElement&&(b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]"),b&&k.$fillElement.css({height:b.scrollHeight+"px"}))}function w(){var a=G[0][J];a!==Q&&(u(),f.$root&&!f.$root.$$phase&&f.$apply()),Q=a}function x(){if(F){for(f.startIndex=0;f.sizesCumulative[f.startIndex]0&&f.startIndex--,f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]').css({position:"relative","min-height":"100%","min-width":"100%"}),h.append(A),e(A)(f),f.$fillElement=A;var N={};c&&(z=b.element('
').on(d,function(a){a.preventDefault(),a.stopPropagation(),a.originalEvent&&(a=a.originalEvent),G[0].scrollLeft+=a.deltaX||-a.wheelDeltaX,G[0].scrollTop+=a.deltaY||-a.wheelDeltaY}).on("mousemove",function(a){(N.x!==a.clientX||N.y!==a.clientY)&&b.element(this).css("display","none"),N={x:a.clientX,y:a.clientY}}).css("display","none"),A.append(z)),f.startIndex=0,f.endIndex=0,G.on("scroll",function(){x()&&f.$apply()}),c&&G.on(d,s),b.element(a).on("resize",t),f.$on("$destroy",function(){b.element(a).off("resize",t)}),f.$on("vsRepeatTrigger",q),f.$on("vsRepeatResize",function(){E=!0,r()});var O,P,Q;f.$watch(function(){"function"==typeof a.requestAnimationFrame?a.requestAnimationFrame(w):w()})}}}}}]),b.element(document.head).append([""].join(""))}(window,window.angular); \ No newline at end of file +!function(a,b){"use strict";var c=-1!==navigator.appVersion.indexOf("Mac"),d="undefined"!=typeof a.onwheel?"wheel":"undefined"!=typeof a.onmousewheel?"mousewheel":"DOMMouseScroll",e=document.documentElement,f=e.matches?"matches":e.matchesSelector?"matchesSelector":e.webkitMatches?"webkitMatches":e.webkitMatchesSelector?"webkitMatchesSelector":e.msMatches?"msMatches":e.msMatchesSelector?"msMatchesSelector":e.mozMatches?"mozMatches":e.mozMatchesSelector?"mozMatchesSelector":null,g=b.element.prototype.closest||function(a){for(var c=this[0].parentNode;c!==document.documentElement&&null!=c&&!c[f](a);)c=c.parentNode;return c&&c[f](a)?b.element(c):b.element()};b.module("vs-repeat",[]).directive("vsRepeat",["$compile",function(e){return{restrict:"A",scope:!0,require:"?^vsRepeat",controller:["$scope",function(a){this.$scrollParent=a.$scrollParent,this.$fillElement=a.$fillElement}],compile:function(f){var h=f.children().eq(0),i=h.attr("ng-repeat"),j=h[0].outerHTML,k=/^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(i),l=k[1],m=k[2],n=k[3],o="$vs_collection",p={vsRepeat:"elementSize",vsOffsetBefore:"offsetBefore",vsOffsetAfter:"offsetAfter",vsExcess:"excess"};return f.empty(),a.getComputedStyle&&"absolute"===a.getComputedStyle(f[0]).position||f.css("position","relative"),{pre:function(f,h,i,k){function q(a,b){if(!D||D.length<1)return f[o]=[],z=0,v(0),void(f.sizesCumulative=[0]);if(z=D.length,G){f.sizes=D.map(function(a){return a[i.vsSizeProperty]});var c=0;f.sizesCumulative=f.sizes.map(function(a){var b=c;return c+=a,b}),f.sizesCumulative.push(c)}r(),u(b)}function r(){F&&f.$$postDigest(function(){if(h[0].offsetHeight||h[0].offsetWidth)for(var a=h.children(),b=0;bb.clientWidth||b.scrollHeight>b.clientHeight)&&A.css("display","block")}function t(){"undefined"!=typeof i.vsAutoresize&&(F=!0,r(),f.$root&&!f.$root.$$phase&&f.$apply()),y()&&f.$apply()}function u(a){P=void 0,Q=void 0,y(a),v(G?f.sizesCumulative[z]:f.elementSize*z),f.$emit("vsRepeatReinitialized",f.startIndex,f.endIndex)}function v(a){if(E){if(B.css({width:f.offsetBefore+a+f.offsetAfter+"px",height:"100%"}),k&&k.$fillElement){var b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]");b&&k.$fillElement.css({width:b.scrollWidth+"px"})}}else B.css({height:f.offsetBefore+a+f.offsetAfter+"px",width:"100%"}),k&&k.$fillElement&&(b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]"),b&&k.$fillElement.css({height:b.scrollHeight+"px"}))}function w(){var a=H[0][K];a!==R&&(u(),f.$root&&!f.$root.$$phase&&f.$apply()),R=a}function x(a){var b=!1;return void 0!==a&&"number"==typeof a&&(b=Math.max(a,0),H[0][M]!==b?H[0][M]=b:b=!1,f.$emit("vsRepeatScrolled",b)),b}function y(a){var b,c,d=!0;if(a&&void 0!==a.startIndex){if(a.startIndexPosition)switch(b=0,typeof a.startIndexPosition){case"number":b=a.startIndexPosition+f.offsetBefore;break;case"string":switch(a.startIndexPosition){case"top":b=f.offsetBefore;break;case"middle":b=(H[0][K]-f.sizes[a.startIndex])/2;break;case"bottom":b=H[0][K]-f.sizes[a.startIndex]-f.offsetAfter;break;case"inview":case"inview#top":case"inview#middle":case"inview#bottom":case"inview#auto":H[0][M]<=f.sizesCumulative[a.startIndex]&&H[0][M]+H[0][K]-f.sizes[a.startIndex]>=f.sizesCumulative[a.startIndex]?(d=!1,b=f.sizesCumulative[a.startIndex]-H[0][M]):(("inview#top"===a.startIndexPosition||"inview"===a.startIndexPosition)&&(b=f.offsetBefore),"inview#bottom"===a.startIndexPosition&&(b=H[0][K]-f.sizes[a.startIndex]+f.offsetAfter),"inview#middle"===a.startIndexPosition&&(b=(H[0][K]-f.sizes[a.startIndex])/2),"inview#auto"===a.startIndexPosition&&(b=H[0][M]<=f.sizesCumulative[a.startIndex]?H[0][K]-f.sizes[a.startIndex]+f.offsetAfter:f.offsetBefore));break;default:console.warn("Incorrect startIndexPosition string value")}break;default:console.warn("Incorrect startIndexPosition type")}else d=!1,b=f.sizesCumulative[a.startIndex]-H[0][M];if(f.startIndex=a.startIndex,G){for(;f.sizesCumulative[f.startIndex]>f.sizesCumulative[a.startIndex]-b;)f.startIndex--;for(c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]a.startIndex*f.elementSize-b;)f.startIndex--;c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=Math.min(f.startIndex+Math.ceil(H[0][K]/f.elementSize)+f.excess/2,z)}}else if(G){for(f.startIndex=0;f.sizesCumulative[f.startIndex]0&&f.startIndex--,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]').css({position:"relative","min-height":"100%","min-width":"100%"}),h.append(B),e(B)(f),f.$fillElement=B;var O={};c&&(A=b.element('
').on(d,function(a){a.preventDefault(),a.stopPropagation(),a.originalEvent&&(a=a.originalEvent),H[0].scrollLeft+=a.deltaX||-a.wheelDeltaX,H[0].scrollTop+=a.deltaY||-a.wheelDeltaY}).on("mousemove",function(a){(O.x!==a.clientX||O.y!==a.clientY)&&b.element(this).css("display","none"),O={x:a.clientX,y:a.clientY}}).css("display","none"),B.append(A)),f.startIndex=0,f.endIndex=0,H.on("scroll",function(){y()&&f.$apply()}),c&&H.on(d,s),b.element(a).on("resize",t),f.$on("$destroy",function(){b.element(a).off("resize",t)}),f.$on("vsRepeatTrigger",q),f.$on("vsRepeatResize",function(){F=!0,r()});var P,Q,R;f.$watch(function(){"function"==typeof a.requestAnimationFrame?a.requestAnimationFrame(w):w()})}}}}}]),b.element(document.head).append([""].join(""))}(window,window.angular); \ No newline at end of file From 3522520edccbfbd18a208cc35a1c947499726878 Mon Sep 17 00:00:00 2001 From: Martin Gruber Date: Tue, 24 Mar 2015 07:34:29 +0100 Subject: [PATCH 2/4] Added: New 'vs-scroll-settings' attribute which is an object affecting the way the items are scrolled in the view. Updated: README.md Comments cleanup. --- README.md | 7 ++- src/angular-vs-repeat.js | 116 +++++++++++++++++------------------ src/angular-vs-repeat.min.js | 2 +- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index ced45b0..49d4425 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ example: - the vsRepeat directive must be applied to a direct parent of an element with `ngRepeat` - the value of vsRepeat attribute is the single element's height/width measured in pixels. If none provided, the directive will compute it automatically - + ###OPTIONAL PARAMETERS (attributes): - `vs-scroll-parent="selector"` - selector to the scrollable container. The directive will look for a closest parent matching the given selector (defaults to the current element) @@ -57,7 +57,8 @@ example: - `vs-excess="value"` - an integer number representing the number of elements to be rendered outside of the current container's viewport (defaults to 2) - `vs-size-property="propertyName"` - a property name of the items in collection that is a number denoting the element size (in pixels) - `vs-autoresize` - use this attribute without `vs-size-property` and without specifying element's size. The automatically computed element style will readjust upon window resize if the size is dependable on the viewport size +- `vs-scroll-settings` - an object with 2 possible properties: `scrollIndex: "value"` - index of the item that should be scrolled to; the exact position of this item in the viewport may be further defined by `scrollIndexPosition: "value"` - a position where the element at `scrollIndex` index will be scrolled to; either a number of pixels or one of the following strings: 'top', 'middle', 'bottom', 'inview' is the same as 'inview#top', 'inview#middle', 'inview#bottom', 'inview#auto'; the 'inview#' settings means that if the item is already in the view, nothing is scrolled, but if it is not, then the item will be scrolled accordingly (to be in the ); position 'auto' means that it will be either 'top' or 'bottom' depending on what is closer to the current item position ###EVENTS: -- `vsRepeatTrigger` - an event the directive listens for to manually trigger reinitialization -- `vsRepeatReinitialized` - an event the directive emits upon reinitialization done +- `vsRepeatTrigger` - an event the directive listens for to manually trigger reinitialization; it may receive additional argument - an object with two properties: `scrollIndex` and `scrollIndexPosition` - their meaning is the same as in the optional attribute `vs-scroll-settings` +- `vsRepeatReinitialized` - an event the directive emits upon reinitialization done; the listener may accepts three arguments: `event`, `startIndex` and `endIndex` diff --git a/src/angular-vs-repeat.js b/src/angular-vs-repeat.js index 0f8e188..042d6df 100644 --- a/src/angular-vs-repeat.js +++ b/src/angular-vs-repeat.js @@ -127,6 +127,8 @@ positioningPropertyTransform = $$horizontal ? 'translateX' : 'translateY', positioningProperty = $$horizontal ? 'left' : 'top', + localScrollTrigger = false, + clientSize = $$horizontal ? 'clientWidth' : 'clientHeight', offsetSize = $$horizontal ? 'offsetWidth' : 'offsetHeight', scrollPos = $$horizontal ? 'scrollLeft' : 'scrollTop'; @@ -141,10 +143,23 @@ $scope.offsetBefore = 0; $scope.offsetAfter = 0; $scope.excess = 2; + $scope.scrollSettings = { + scrollIndex: 0, + scrollIndexPosition: 'top', + }; + + $scope.$watch($attrs.vsScrollSettings, function(newValue, oldValue) { + if (typeof newValue === 'undefined') { + return; + } + $scope.scrollSettings = newValue; + reinitialize($scope.scrollSettings); + }, true); Object.keys(attributesDictionary).forEach(function(key){ if($attrs[key]){ $attrs.$observe(key, function(value){ + // '+' serves for getting a number from the string as the attributes are always strings $scope[attributesDictionary[key]] = +value; reinitialize(); }); @@ -273,9 +288,15 @@ $scope.endIndex = 0; $scrollParent.on('scroll', function scrollHandler(e){ - // console.log('Parent scrolling: ', $scrollParent[0][scrollPos]); - if(updateInnerCollection()) { - $scope.$apply(); + // Check if the scrolling was triggerred by a local action to avoid + // unnecessary inner collection updating + if (localScrollTrigger) { + localScrollTrigger = false; + } + else { + if(updateInnerCollection()) { + $scope.$apply(); + } } }); @@ -323,7 +344,6 @@ $scope.sizesCumulative[originalLength] : $scope.elementSize*originalLength ); - // console.log('Emitting the vsRepeatReinitialized event, $scope.startIndex: ', $scope.startIndex, ', $scope.endIndex: ', $scope.endIndex); $scope.$emit('vsRepeatReinitialized', $scope.startIndex, $scope.endIndex); } @@ -386,65 +406,54 @@ if (scrollTo !== undefined && (typeof scrollTo) === 'number') { // Set the position to be scrolled to scrolled = Math.max(scrollTo, 0); - // console.log('Scrolling to ' + scrolled + 'px'); // Is there a scroll change? if ($scrollParent[0][scrollPos] !== scrolled) { $scrollParent[0][scrollPos] = scrolled; + localScrollTrigger = true; } else { scrolled = false; } // Emit the event - // console.log('Emitting the vsRepeatScrolled event, scrolled: ', scrolled); $scope.$emit('vsRepeatScrolled', scrolled); } return scrolled; } - // Update the inner collection, i.e. do the proper slicing - // data - an object with the following properties: - // * startIndex - index of the item that should be considered as the first item; - // it is not necessarily the one that will be rendered at the first position, - // the first rendered item depends also on the startIndexPosition - // of the item at startIndex - // * startIndexPosition - position of the first item, i.e. how much it should - // be scrolled; if not provided, no scrolling is performed - // - either a number of pixels or one of the following strings: - // 'top', 'middle', 'bottom', 'inview' is the same as 'inview#top', - // 'inview#middle', 'inview#bottom', 'inview#auto'; - // the 'inview#' settings means that if the item is - // already in the view, nothing is scrolled, but if it is not, - // then the item will be scrolled accordingly (to be in the ); - // position 'auto' means that it will be either 'top' or 'bottom' - // depending on what is closer to the current item position function updateInnerCollection(data){ var scrollChange = true, position, visibleStartIndex; - if (data && data.startIndex !== undefined) { + + if (data && data.scrollIndex !== undefined) { + if (typeof $scope.scrollSettings !== 'undefined') { + $scope.scrollSettings.scrollIndex = data.scrollIndex; + } // Item scroll position relative to the view, i.e. position === 0 means the top of the view, // position === $scrollParent[0][clientSize] means the bottom - if (data.startIndexPosition) { - // console.log('Required start index position: ', data.startIndexPosition); + if (data.scrollIndexPosition !== undefined) { + if (typeof $scope.scrollSettings !== 'undefined') { + $scope.scrollSettings.scrollIndexPosition = data.scrollIndexPosition; + } position = 0; - switch (typeof data.startIndexPosition) { + switch (typeof data.scrollIndexPosition) { case 'number': - position = data.startIndexPosition + $scope.offsetBefore; + position = data.scrollIndexPosition + $scope.offsetBefore; break; case 'string': - switch (data.startIndexPosition) { + switch (data.scrollIndexPosition) { case 'top': position = $scope.offsetBefore; break; case 'middle': - position = ($scrollParent[0][clientSize] - $scope.sizes[data.startIndex]) / 2; + position = ($scrollParent[0][clientSize] - $scope.sizes[data.scrollIndex]) / 2; break; case 'bottom': - position = $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] - $scope.offsetAfter; + position = $scrollParent[0][clientSize] - $scope.sizes[data.scrollIndex] - $scope.offsetAfter; break; case 'inview': case 'inview#top': @@ -453,30 +462,30 @@ case 'inview#auto': // The item is in the viewport, do nothing if ( - ($scrollParent[0][scrollPos] <= ($scope.sizesCumulative[data.startIndex])) && - ($scrollParent[0][scrollPos] + $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] >= $scope.sizesCumulative[data.startIndex])) { + ($scrollParent[0][scrollPos] <= ($scope.sizesCumulative[data.scrollIndex])) && + ($scrollParent[0][scrollPos] + $scrollParent[0][clientSize] - $scope.sizes[data.scrollIndex] >= $scope.sizesCumulative[data.scrollIndex])) { scrollChange = false; // The current item scroll position - position = $scope.sizesCumulative[data.startIndex] - $scrollParent[0][scrollPos]; + position = $scope.sizesCumulative[data.scrollIndex] - $scrollParent[0][scrollPos]; } // The item is out of the viewport else { - if (data.startIndexPosition === 'inview#top' || data.startIndexPosition === 'inview') { + if (data.scrollIndexPosition === 'inview#top' || data.scrollIndexPosition === 'inview') { // Get it at the top position = $scope.offsetBefore; } - if (data.startIndexPosition === 'inview#bottom') { + if (data.scrollIndexPosition === 'inview#bottom') { // Get it at the bottom - position = $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] + $scope.offsetAfter; + position = $scrollParent[0][clientSize] - $scope.sizes[data.scrollIndex] + $scope.offsetAfter; } - if (data.startIndexPosition === 'inview#middle') { + if (data.scrollIndexPosition === 'inview#middle') { // Get it at the middle - position = ($scrollParent[0][clientSize] - $scope.sizes[data.startIndex]) / 2; + position = ($scrollParent[0][clientSize] - $scope.sizes[data.scrollIndex]) / 2; } - if (data.startIndexPosition === 'inview#auto') { + if (data.scrollIndexPosition === 'inview#auto') { // Get it at the bottom or at the top, depending on what is closer - if ($scrollParent[0][scrollPos] <= $scope.sizesCumulative[data.startIndex]) { - position = $scrollParent[0][clientSize] - $scope.sizes[data.startIndex] + $scope.offsetAfter; + if ($scrollParent[0][scrollPos] <= $scope.sizesCumulative[data.scrollIndex]) { + position = $scrollParent[0][clientSize] - $scope.sizes[data.scrollIndex] + $scope.offsetAfter; } else { position = $scope.offsetBefore; @@ -485,12 +494,12 @@ } break; default: - console.warn('Incorrect startIndexPosition string value'); + console.warn('Incorrect scrollIndexPosition string value'); break; } break; default: - console.warn('Incorrect startIndexPosition type'); + console.warn('Incorrect scrollIndexPosition type'); break; } } @@ -498,18 +507,14 @@ // The item is not required to be in the viewport, do nothing scrollChange = false; // The current item scroll position - position = $scope.sizesCumulative[data.startIndex] - $scrollParent[0][scrollPos]; + position = $scope.sizesCumulative[data.scrollIndex] - $scrollParent[0][scrollPos]; } - // console.log('Required start index: ', data.startIndex); - // console.log('Item position in the view: ', position); - // console.log('Cumulative size for start index: ', $scope.sizesCumulative[data.startIndex]); - - $scope.startIndex = data.startIndex; + $scope.startIndex = data.scrollIndex; if(sizesPropertyExists){ - while($scope.sizesCumulative[$scope.startIndex] > $scope.sizesCumulative[data.startIndex] - position) + while($scope.sizesCumulative[$scope.startIndex] > $scope.sizesCumulative[data.scrollIndex] - position) { $scope.startIndex--; } @@ -534,7 +539,7 @@ } else { - while(($scope.startIndex * $scope.elementSize) > (data.startIndex * $scope.elementSize) - position) + while(($scope.startIndex * $scope.elementSize) > (data.scrollIndex * $scope.elementSize) - position) { $scope.startIndex--; } @@ -594,22 +599,17 @@ } var scrolled = false; - if (data !== undefined && data.startIndex !== undefined && position !== undefined && scrollChange) { + if (data !== undefined && data.scrollIndex !== undefined && position !== undefined && scrollChange) { // Scroll to the requested position - scrolled = scrollToPosition($scope.sizesCumulative[data.startIndex] - position); + scrolled = scrollToPosition($scope.sizesCumulative[data.scrollIndex] - position); } - // console.log('Real viewport start index: ', $scope.startIndex); - // console.log('Real viewport end index: ', $scope.endIndex); - // console.log('Scroll position: ', $scrollParent[0][scrollPos]); - var digestRequired = $scope.startIndex !== _prevStartIndex || $scope.endIndex !== _prevEndIndex; if(digestRequired) { $scope[collectionName] = originalCollection.slice($scope.startIndex, $scope.endIndex); // Emit the event - // console.log('Emitting the vsRepeatInnerCollectionUpdated event, $scope.startIndex: ', $scope.startIndex, ', $scope.endIndex: ', $scope.endIndex, ', _prevStartIndex: ', _prevStartIndex, ', _prevEndIndex: ', _prevEndIndex); $scope.$emit('vsRepeatInnerCollectionUpdated', $scope.startIndex, $scope.endIndex, _prevStartIndex, _prevEndIndex); } diff --git a/src/angular-vs-repeat.min.js b/src/angular-vs-repeat.min.js index 881b2f8..342ca29 100644 --- a/src/angular-vs-repeat.min.js +++ b/src/angular-vs-repeat.min.js @@ -1 +1 @@ -!function(a,b){"use strict";var c=-1!==navigator.appVersion.indexOf("Mac"),d="undefined"!=typeof a.onwheel?"wheel":"undefined"!=typeof a.onmousewheel?"mousewheel":"DOMMouseScroll",e=document.documentElement,f=e.matches?"matches":e.matchesSelector?"matchesSelector":e.webkitMatches?"webkitMatches":e.webkitMatchesSelector?"webkitMatchesSelector":e.msMatches?"msMatches":e.msMatchesSelector?"msMatchesSelector":e.mozMatches?"mozMatches":e.mozMatchesSelector?"mozMatchesSelector":null,g=b.element.prototype.closest||function(a){for(var c=this[0].parentNode;c!==document.documentElement&&null!=c&&!c[f](a);)c=c.parentNode;return c&&c[f](a)?b.element(c):b.element()};b.module("vs-repeat",[]).directive("vsRepeat",["$compile",function(e){return{restrict:"A",scope:!0,require:"?^vsRepeat",controller:["$scope",function(a){this.$scrollParent=a.$scrollParent,this.$fillElement=a.$fillElement}],compile:function(f){var h=f.children().eq(0),i=h.attr("ng-repeat"),j=h[0].outerHTML,k=/^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(i),l=k[1],m=k[2],n=k[3],o="$vs_collection",p={vsRepeat:"elementSize",vsOffsetBefore:"offsetBefore",vsOffsetAfter:"offsetAfter",vsExcess:"excess"};return f.empty(),a.getComputedStyle&&"absolute"===a.getComputedStyle(f[0]).position||f.css("position","relative"),{pre:function(f,h,i,k){function q(a,b){if(!D||D.length<1)return f[o]=[],z=0,v(0),void(f.sizesCumulative=[0]);if(z=D.length,G){f.sizes=D.map(function(a){return a[i.vsSizeProperty]});var c=0;f.sizesCumulative=f.sizes.map(function(a){var b=c;return c+=a,b}),f.sizesCumulative.push(c)}r(),u(b)}function r(){F&&f.$$postDigest(function(){if(h[0].offsetHeight||h[0].offsetWidth)for(var a=h.children(),b=0;bb.clientWidth||b.scrollHeight>b.clientHeight)&&A.css("display","block")}function t(){"undefined"!=typeof i.vsAutoresize&&(F=!0,r(),f.$root&&!f.$root.$$phase&&f.$apply()),y()&&f.$apply()}function u(a){P=void 0,Q=void 0,y(a),v(G?f.sizesCumulative[z]:f.elementSize*z),f.$emit("vsRepeatReinitialized",f.startIndex,f.endIndex)}function v(a){if(E){if(B.css({width:f.offsetBefore+a+f.offsetAfter+"px",height:"100%"}),k&&k.$fillElement){var b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]");b&&k.$fillElement.css({width:b.scrollWidth+"px"})}}else B.css({height:f.offsetBefore+a+f.offsetAfter+"px",width:"100%"}),k&&k.$fillElement&&(b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]"),b&&k.$fillElement.css({height:b.scrollHeight+"px"}))}function w(){var a=H[0][K];a!==R&&(u(),f.$root&&!f.$root.$$phase&&f.$apply()),R=a}function x(a){var b=!1;return void 0!==a&&"number"==typeof a&&(b=Math.max(a,0),H[0][M]!==b?H[0][M]=b:b=!1,f.$emit("vsRepeatScrolled",b)),b}function y(a){var b,c,d=!0;if(a&&void 0!==a.startIndex){if(a.startIndexPosition)switch(b=0,typeof a.startIndexPosition){case"number":b=a.startIndexPosition+f.offsetBefore;break;case"string":switch(a.startIndexPosition){case"top":b=f.offsetBefore;break;case"middle":b=(H[0][K]-f.sizes[a.startIndex])/2;break;case"bottom":b=H[0][K]-f.sizes[a.startIndex]-f.offsetAfter;break;case"inview":case"inview#top":case"inview#middle":case"inview#bottom":case"inview#auto":H[0][M]<=f.sizesCumulative[a.startIndex]&&H[0][M]+H[0][K]-f.sizes[a.startIndex]>=f.sizesCumulative[a.startIndex]?(d=!1,b=f.sizesCumulative[a.startIndex]-H[0][M]):(("inview#top"===a.startIndexPosition||"inview"===a.startIndexPosition)&&(b=f.offsetBefore),"inview#bottom"===a.startIndexPosition&&(b=H[0][K]-f.sizes[a.startIndex]+f.offsetAfter),"inview#middle"===a.startIndexPosition&&(b=(H[0][K]-f.sizes[a.startIndex])/2),"inview#auto"===a.startIndexPosition&&(b=H[0][M]<=f.sizesCumulative[a.startIndex]?H[0][K]-f.sizes[a.startIndex]+f.offsetAfter:f.offsetBefore));break;default:console.warn("Incorrect startIndexPosition string value")}break;default:console.warn("Incorrect startIndexPosition type")}else d=!1,b=f.sizesCumulative[a.startIndex]-H[0][M];if(f.startIndex=a.startIndex,G){for(;f.sizesCumulative[f.startIndex]>f.sizesCumulative[a.startIndex]-b;)f.startIndex--;for(c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]a.startIndex*f.elementSize-b;)f.startIndex--;c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=Math.min(f.startIndex+Math.ceil(H[0][K]/f.elementSize)+f.excess/2,z)}}else if(G){for(f.startIndex=0;f.sizesCumulative[f.startIndex]0&&f.startIndex--,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]').css({position:"relative","min-height":"100%","min-width":"100%"}),h.append(B),e(B)(f),f.$fillElement=B;var O={};c&&(A=b.element('
').on(d,function(a){a.preventDefault(),a.stopPropagation(),a.originalEvent&&(a=a.originalEvent),H[0].scrollLeft+=a.deltaX||-a.wheelDeltaX,H[0].scrollTop+=a.deltaY||-a.wheelDeltaY}).on("mousemove",function(a){(O.x!==a.clientX||O.y!==a.clientY)&&b.element(this).css("display","none"),O={x:a.clientX,y:a.clientY}}).css("display","none"),B.append(A)),f.startIndex=0,f.endIndex=0,H.on("scroll",function(){y()&&f.$apply()}),c&&H.on(d,s),b.element(a).on("resize",t),f.$on("$destroy",function(){b.element(a).off("resize",t)}),f.$on("vsRepeatTrigger",q),f.$on("vsRepeatResize",function(){F=!0,r()});var P,Q,R;f.$watch(function(){"function"==typeof a.requestAnimationFrame?a.requestAnimationFrame(w):w()})}}}}}]),b.element(document.head).append([""].join(""))}(window,window.angular); \ No newline at end of file +!function(a,b){"use strict";var c=-1!==navigator.appVersion.indexOf("Mac"),d="undefined"!=typeof a.onwheel?"wheel":"undefined"!=typeof a.onmousewheel?"mousewheel":"DOMMouseScroll",e=document.documentElement,f=e.matches?"matches":e.matchesSelector?"matchesSelector":e.webkitMatches?"webkitMatches":e.webkitMatchesSelector?"webkitMatchesSelector":e.msMatches?"msMatches":e.msMatchesSelector?"msMatchesSelector":e.mozMatches?"mozMatches":e.mozMatchesSelector?"mozMatchesSelector":null,g=b.element.prototype.closest||function(a){for(var c=this[0].parentNode;c!==document.documentElement&&null!=c&&!c[f](a);)c=c.parentNode;return c&&c[f](a)?b.element(c):b.element()};b.module("vs-repeat",[]).directive("vsRepeat",["$compile",function(e){return{restrict:"A",scope:!0,require:"?^vsRepeat",controller:["$scope",function(a){this.$scrollParent=a.$scrollParent,this.$fillElement=a.$fillElement}],compile:function(f){var h=f.children().eq(0),i=h.attr("ng-repeat"),j=h[0].outerHTML,k=/^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(i),l=k[1],m=k[2],n=k[3],o="$vs_collection",p={vsRepeat:"elementSize",vsOffsetBefore:"offsetBefore",vsOffsetAfter:"offsetAfter",vsExcess:"excess"};return f.empty(),a.getComputedStyle&&"absolute"===a.getComputedStyle(f[0]).position||f.css("position","relative"),{pre:function(f,h,i,k){function q(a,b){if(!D||D.length<1)return f[o]=[],z=0,v(0),void(f.sizesCumulative=[0]);if(z=D.length,G){f.sizes=D.map(function(a){return a[i.vsSizeProperty]});var c=0;f.sizesCumulative=f.sizes.map(function(a){var b=c;return c+=a,b}),f.sizesCumulative.push(c)}r(),u(b)}function r(){F&&f.$$postDigest(function(){if(h[0].offsetHeight||h[0].offsetWidth)for(var a=h.children(),b=0;bb.clientWidth||b.scrollHeight>b.clientHeight)&&A.css("display","block")}function t(){"undefined"!=typeof i.vsAutoresize&&(F=!0,r(),f.$root&&!f.$root.$$phase&&f.$apply()),y()&&f.$apply()}function u(a){Q=void 0,R=void 0,y(a),v(G?f.sizesCumulative[z]:f.elementSize*z),f.$emit("vsRepeatReinitialized",f.startIndex,f.endIndex)}function v(a){if(E){if(B.css({width:f.offsetBefore+a+f.offsetAfter+"px",height:"100%"}),k&&k.$fillElement){var b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]");b&&k.$fillElement.css({width:b.scrollWidth+"px"})}}else B.css({height:f.offsetBefore+a+f.offsetAfter+"px",width:"100%"}),k&&k.$fillElement&&(b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]"),b&&k.$fillElement.css({height:b.scrollHeight+"px"}))}function w(){var a=H[0][L];a!==S&&(u(),f.$root&&!f.$root.$$phase&&f.$apply()),S=a}function x(a){var b=!1;return void 0!==a&&"number"==typeof a&&(b=Math.max(a,0),H[0][N]!==b?(H[0][N]=b,K=!0):b=!1,f.$emit("vsRepeatScrolled",b)),b}function y(a){var b,c,d=!0;if(a&&void 0!==a.scrollIndex){if("undefined"!=typeof f.scrollSettings&&(f.scrollSettings.scrollIndex=a.scrollIndex),void 0!==a.scrollIndexPosition)switch("undefined"!=typeof f.scrollSettings&&(f.scrollSettings.scrollIndexPosition=a.scrollIndexPosition),b=0,typeof a.scrollIndexPosition){case"number":b=a.scrollIndexPosition+f.offsetBefore;break;case"string":switch(a.scrollIndexPosition){case"top":b=f.offsetBefore;break;case"middle":b=(H[0][L]-f.sizes[a.scrollIndex])/2;break;case"bottom":b=H[0][L]-f.sizes[a.scrollIndex]-f.offsetAfter;break;case"inview":case"inview#top":case"inview#middle":case"inview#bottom":case"inview#auto":H[0][N]<=f.sizesCumulative[a.scrollIndex]&&H[0][N]+H[0][L]-f.sizes[a.scrollIndex]>=f.sizesCumulative[a.scrollIndex]?(d=!1,b=f.sizesCumulative[a.scrollIndex]-H[0][N]):(("inview#top"===a.scrollIndexPosition||"inview"===a.scrollIndexPosition)&&(b=f.offsetBefore),"inview#bottom"===a.scrollIndexPosition&&(b=H[0][L]-f.sizes[a.scrollIndex]+f.offsetAfter),"inview#middle"===a.scrollIndexPosition&&(b=(H[0][L]-f.sizes[a.scrollIndex])/2),"inview#auto"===a.scrollIndexPosition&&(b=H[0][N]<=f.sizesCumulative[a.scrollIndex]?H[0][L]-f.sizes[a.scrollIndex]+f.offsetAfter:f.offsetBefore));break;default:console.warn("Incorrect scrollIndexPosition string value")}break;default:console.warn("Incorrect scrollIndexPosition type")}else d=!1,b=f.sizesCumulative[a.scrollIndex]-H[0][N];if(f.startIndex=a.scrollIndex,G){for(;f.sizesCumulative[f.startIndex]>f.sizesCumulative[a.scrollIndex]-b;)f.startIndex--;for(c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]a.scrollIndex*f.elementSize-b;)f.startIndex--;c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=Math.min(f.startIndex+Math.ceil(H[0][L]/f.elementSize)+f.excess/2,z)}}else if(G){for(f.startIndex=0;f.sizesCumulative[f.startIndex]0&&f.startIndex--,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]').css({position:"relative","min-height":"100%","min-width":"100%"}),h.append(B),e(B)(f),f.$fillElement=B;var P={};c&&(A=b.element('
').on(d,function(a){a.preventDefault(),a.stopPropagation(),a.originalEvent&&(a=a.originalEvent),H[0].scrollLeft+=a.deltaX||-a.wheelDeltaX,H[0].scrollTop+=a.deltaY||-a.wheelDeltaY}).on("mousemove",function(a){(P.x!==a.clientX||P.y!==a.clientY)&&b.element(this).css("display","none"),P={x:a.clientX,y:a.clientY}}).css("display","none"),B.append(A)),f.startIndex=0,f.endIndex=0,H.on("scroll",function(){K?K=!1:y()&&f.$apply()}),c&&H.on(d,s),b.element(a).on("resize",t),f.$on("$destroy",function(){b.element(a).off("resize",t)}),f.$on("vsRepeatTrigger",q),f.$on("vsRepeatResize",function(){F=!0,r()});var Q,R,S;f.$watch(function(){"function"==typeof a.requestAnimationFrame?a.requestAnimationFrame(w):w()})}}}}}]),b.element(document.head).append([""].join(""))}(window,window.angular); \ No newline at end of file From 6fd0e8a2bda4bc9896bd96acf8557c979cd58c8f Mon Sep 17 00:00:00 2001 From: Martin Gruber Date: Tue, 24 Mar 2015 08:12:20 +0100 Subject: [PATCH 3/4] Added: Proper quoting in the README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49d4425..57c4c04 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ example: - `vs-excess="value"` - an integer number representing the number of elements to be rendered outside of the current container's viewport (defaults to 2) - `vs-size-property="propertyName"` - a property name of the items in collection that is a number denoting the element size (in pixels) - `vs-autoresize` - use this attribute without `vs-size-property` and without specifying element's size. The automatically computed element style will readjust upon window resize if the size is dependable on the viewport size -- `vs-scroll-settings` - an object with 2 possible properties: `scrollIndex: "value"` - index of the item that should be scrolled to; the exact position of this item in the viewport may be further defined by `scrollIndexPosition: "value"` - a position where the element at `scrollIndex` index will be scrolled to; either a number of pixels or one of the following strings: 'top', 'middle', 'bottom', 'inview' is the same as 'inview#top', 'inview#middle', 'inview#bottom', 'inview#auto'; the 'inview#' settings means that if the item is already in the view, nothing is scrolled, but if it is not, then the item will be scrolled accordingly (to be in the ); position 'auto' means that it will be either 'top' or 'bottom' depending on what is closer to the current item position +- `vs-scroll-settings` - an object with 2 possible properties: `scrollIndex: "value"` - index of the item that should be scrolled to; the exact position of this item in the viewport may be further defined by `scrollIndexPosition: "value"` - a position where the element at `scrollIndex` index will be scrolled to; either a number of pixels or one of the following strings: 'top', 'middle', 'bottom', 'inview' is the same as 'inview#top', 'inview#middle', 'inview#bottom', 'inview#auto'; the 'inview#\' settings means that if the item is already in the view, nothing is scrolled, but if it is not, then the item will be scrolled accordingly (to be in the \); position 'auto' means that it will be either 'top' or 'bottom' depending on what is closer to the current item position ###EVENTS: - `vsRepeatTrigger` - an event the directive listens for to manually trigger reinitialization; it may receive additional argument - an object with two properties: `scrollIndex` and `scrollIndexPosition` - their meaning is the same as in the optional attribute `vs-scroll-settings` From d1cc180bff361648e7c4b6ae376c5401a390d35a Mon Sep 17 00:00:00 2001 From: Martin Gruber Date: Wed, 25 Mar 2015 09:00:34 +0100 Subject: [PATCH 4/4] Updated: Reverted changes regarding the code-style. --- src/angular-vs-repeat.js | 63 ++++++++++++++---------------------- src/angular-vs-repeat.min.js | 2 +- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/angular-vs-repeat.js b/src/angular-vs-repeat.js index 042d6df..1e9ff30 100644 --- a/src/angular-vs-repeat.js +++ b/src/angular-vs-repeat.js @@ -58,7 +58,7 @@ // - 'vsRepeatTrigger' - an event the directive listens for to manually trigger reinitialization // - 'vsRepeatReinitialized' - an event the directive emits upon reinitialization done - var isMacOS = navigator.appVersion.indexOf('Mac') !== -1, + var isMacOS = navigator.appVersion.indexOf('Mac') != -1, wheelEventName = typeof window.onwheel !== 'undefined' ? 'wheel' : typeof window.onmousewheel !== 'undefined' ? 'mousewheel' : 'DOMMouseScroll', dde = document.documentElement, matchingFunction = dde.matches ? 'matches' : @@ -76,12 +76,10 @@ el = el.parentNode; } - if(el && el[matchingFunction](selector)) { + if(el && el[matchingFunction](selector)) return angular.element(el); - } - else { + else return angular.element(); - } }; angular.module('vs-repeat', []).directive('vsRepeat', ['$compile', function($compile){ @@ -110,15 +108,14 @@ }; $element.empty(); - if(!window.getComputedStyle || window.getComputedStyle($element[0]).position !== 'absolute') { + if(!window.getComputedStyle || window.getComputedStyle($element[0]).position !== 'absolute') $element.css('position', 'relative'); - } return { pre: function($scope, $element, $attrs, $ctrl){ var childClone = angular.element(childCloneHtml), originalCollection = [], originalLength, - $$horizontal = typeof $attrs.vsHorizontal !== 'undefined', + $$horizontal = typeof $attrs.vsHorizontal !== "undefined", $wheelHelper, $fillElement, autoSize = !$attrs.vsRepeat, @@ -133,10 +130,10 @@ offsetSize = $$horizontal ? 'offsetWidth' : 'offsetHeight', scrollPos = $$horizontal ? 'scrollLeft' : 'scrollTop'; - if($scrollParent.length === 0) { throw 'Specified scroll parent selector did not match any element'; } + if($scrollParent.length === 0) throw 'Specified scroll parent selector did not match any element'; $scope.$scrollParent = $scrollParent; - if(sizesPropertyExists) { $scope.sizesCumulative = []; } + if(sizesPropertyExists) $scope.sizesCumulative = []; //initial defaults $scope.elementSize = $scrollParent[0][clientSize] || 50; @@ -212,9 +209,8 @@ $scope.elementSize = children[i][offsetSize]; reinitialize(); autoSize = false; - if($scope.$root && !$scope.$root.$$phase) { + if($scope.$root && !$scope.$root.$$phase) $scope.$apply(); - } } break; } @@ -240,10 +236,10 @@ '(sizesCumulative[$index + startIndex] + offsetBefore)' : '(($index + startIndex) * elementSize + offsetBefore)'; - if(typeof document.documentElement.style.transform !== 'undefined'){ // browser supports transform css property + if(typeof document.documentElement.style.transform !== "undefined"){ // browser supports transform css property childClone.attr('ng-style', '{ "transform": "' + positioningPropertyTransform + '(" + ' + offsetCalculationString + ' + "px)"}'); } - else if(typeof document.documentElement.style.webkitTransform !== 'undefined'){ // browser supports -webkit-transform css property + else if(typeof document.documentElement.style.webkitTransform !== "undefined"){ // browser supports -webkit-transform css property childClone.attr('ng-style', '{ "-webkit-transform": "' + positioningPropertyTransform + '(" + ' + offsetCalculationString + ' + "px)"}'); } else{ @@ -264,18 +260,17 @@ $scope.$fillElement = $fillElement; var _prevMouse = {}; - if(isMacOS) { + if(isMacOS){ $wheelHelper = angular.element('
') .on(wheelEventName, function(e){ e.preventDefault(); e.stopPropagation(); - if(e.originalEvent) { e = e.originalEvent; } + if(e.originalEvent) e = e.originalEvent; $scrollParent[0].scrollLeft += (e.deltaX || -e.wheelDeltaX); $scrollParent[0].scrollTop += (e.deltaY || -e.wheelDeltaY); }).on('mousemove', function(e){ - if(_prevMouse.x !== e.clientX || _prevMouse.y !== e.clientY) { + if(_prevMouse.x !== e.clientX || _prevMouse.y !== e.clientY) angular.element(this).css('display', 'none'); - } _prevMouse = { x: e.clientX, y: e.clientY @@ -300,27 +295,24 @@ } }); - function wheelHandler(e) { - var elem = e.currentTarget; - if(elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight) { - $wheelHelper.css('display', 'block'); - } - } if(isMacOS){ $scrollParent.on(wheelEventName, wheelHandler); } + function wheelHandler(e){ + var elem = e.currentTarget; + if(elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight) + $wheelHelper.css('display', 'block'); + } function onWindowResize(){ if(typeof $attrs.vsAutoresize !== 'undefined'){ autoSize = true; setAutoSize(); - if($scope.$root && !$scope.$root.$$phase) { + if($scope.$root && !$scope.$root.$$phase) $scope.$apply(); - } } - if(updateInnerCollection()) { + if(updateInnerCollection()) $scope.$apply(); - } } angular.element(window).on('resize', onWindowResize); @@ -355,11 +347,10 @@ }); if($ctrl && $ctrl.$fillElement){ var referenceElement = $ctrl.$fillElement[0].parentNode.querySelector('[ng-repeat]'); - if(referenceElement) { + if(referenceElement) $ctrl.$fillElement.css({ 'width': referenceElement.scrollWidth + 'px' }); - } } } else{ @@ -369,11 +360,10 @@ }); if($ctrl && $ctrl.$fillElement){ referenceElement = $ctrl.$fillElement[0].parentNode.querySelector('[ng-repeat]'); - if(referenceElement) { + if(referenceElement) $ctrl.$fillElement.css({ 'height': referenceElement.scrollHeight + 'px' }); - } } } } @@ -383,20 +373,17 @@ var ch = $scrollParent[0][clientSize]; if(ch !== _prevClientSize){ reinitialize(); - if($scope.$root && !$scope.$root.$$phase) { + if($scope.$root && !$scope.$root.$$phase) $scope.$apply(); - } } _prevClientSize = ch; } $scope.$watch(function(){ - if(typeof window.requestAnimationFrame === 'function') { + if(typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(reinitOnClientHeightChange); - } - else { + else reinitOnClientHeightChange(); - } }); // Scroll to required position diff --git a/src/angular-vs-repeat.min.js b/src/angular-vs-repeat.min.js index 342ca29..57f7b95 100644 --- a/src/angular-vs-repeat.min.js +++ b/src/angular-vs-repeat.min.js @@ -1 +1 @@ -!function(a,b){"use strict";var c=-1!==navigator.appVersion.indexOf("Mac"),d="undefined"!=typeof a.onwheel?"wheel":"undefined"!=typeof a.onmousewheel?"mousewheel":"DOMMouseScroll",e=document.documentElement,f=e.matches?"matches":e.matchesSelector?"matchesSelector":e.webkitMatches?"webkitMatches":e.webkitMatchesSelector?"webkitMatchesSelector":e.msMatches?"msMatches":e.msMatchesSelector?"msMatchesSelector":e.mozMatches?"mozMatches":e.mozMatchesSelector?"mozMatchesSelector":null,g=b.element.prototype.closest||function(a){for(var c=this[0].parentNode;c!==document.documentElement&&null!=c&&!c[f](a);)c=c.parentNode;return c&&c[f](a)?b.element(c):b.element()};b.module("vs-repeat",[]).directive("vsRepeat",["$compile",function(e){return{restrict:"A",scope:!0,require:"?^vsRepeat",controller:["$scope",function(a){this.$scrollParent=a.$scrollParent,this.$fillElement=a.$fillElement}],compile:function(f){var h=f.children().eq(0),i=h.attr("ng-repeat"),j=h[0].outerHTML,k=/^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(i),l=k[1],m=k[2],n=k[3],o="$vs_collection",p={vsRepeat:"elementSize",vsOffsetBefore:"offsetBefore",vsOffsetAfter:"offsetAfter",vsExcess:"excess"};return f.empty(),a.getComputedStyle&&"absolute"===a.getComputedStyle(f[0]).position||f.css("position","relative"),{pre:function(f,h,i,k){function q(a,b){if(!D||D.length<1)return f[o]=[],z=0,v(0),void(f.sizesCumulative=[0]);if(z=D.length,G){f.sizes=D.map(function(a){return a[i.vsSizeProperty]});var c=0;f.sizesCumulative=f.sizes.map(function(a){var b=c;return c+=a,b}),f.sizesCumulative.push(c)}r(),u(b)}function r(){F&&f.$$postDigest(function(){if(h[0].offsetHeight||h[0].offsetWidth)for(var a=h.children(),b=0;bb.clientWidth||b.scrollHeight>b.clientHeight)&&A.css("display","block")}function t(){"undefined"!=typeof i.vsAutoresize&&(F=!0,r(),f.$root&&!f.$root.$$phase&&f.$apply()),y()&&f.$apply()}function u(a){Q=void 0,R=void 0,y(a),v(G?f.sizesCumulative[z]:f.elementSize*z),f.$emit("vsRepeatReinitialized",f.startIndex,f.endIndex)}function v(a){if(E){if(B.css({width:f.offsetBefore+a+f.offsetAfter+"px",height:"100%"}),k&&k.$fillElement){var b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]");b&&k.$fillElement.css({width:b.scrollWidth+"px"})}}else B.css({height:f.offsetBefore+a+f.offsetAfter+"px",width:"100%"}),k&&k.$fillElement&&(b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]"),b&&k.$fillElement.css({height:b.scrollHeight+"px"}))}function w(){var a=H[0][L];a!==S&&(u(),f.$root&&!f.$root.$$phase&&f.$apply()),S=a}function x(a){var b=!1;return void 0!==a&&"number"==typeof a&&(b=Math.max(a,0),H[0][N]!==b?(H[0][N]=b,K=!0):b=!1,f.$emit("vsRepeatScrolled",b)),b}function y(a){var b,c,d=!0;if(a&&void 0!==a.scrollIndex){if("undefined"!=typeof f.scrollSettings&&(f.scrollSettings.scrollIndex=a.scrollIndex),void 0!==a.scrollIndexPosition)switch("undefined"!=typeof f.scrollSettings&&(f.scrollSettings.scrollIndexPosition=a.scrollIndexPosition),b=0,typeof a.scrollIndexPosition){case"number":b=a.scrollIndexPosition+f.offsetBefore;break;case"string":switch(a.scrollIndexPosition){case"top":b=f.offsetBefore;break;case"middle":b=(H[0][L]-f.sizes[a.scrollIndex])/2;break;case"bottom":b=H[0][L]-f.sizes[a.scrollIndex]-f.offsetAfter;break;case"inview":case"inview#top":case"inview#middle":case"inview#bottom":case"inview#auto":H[0][N]<=f.sizesCumulative[a.scrollIndex]&&H[0][N]+H[0][L]-f.sizes[a.scrollIndex]>=f.sizesCumulative[a.scrollIndex]?(d=!1,b=f.sizesCumulative[a.scrollIndex]-H[0][N]):(("inview#top"===a.scrollIndexPosition||"inview"===a.scrollIndexPosition)&&(b=f.offsetBefore),"inview#bottom"===a.scrollIndexPosition&&(b=H[0][L]-f.sizes[a.scrollIndex]+f.offsetAfter),"inview#middle"===a.scrollIndexPosition&&(b=(H[0][L]-f.sizes[a.scrollIndex])/2),"inview#auto"===a.scrollIndexPosition&&(b=H[0][N]<=f.sizesCumulative[a.scrollIndex]?H[0][L]-f.sizes[a.scrollIndex]+f.offsetAfter:f.offsetBefore));break;default:console.warn("Incorrect scrollIndexPosition string value")}break;default:console.warn("Incorrect scrollIndexPosition type")}else d=!1,b=f.sizesCumulative[a.scrollIndex]-H[0][N];if(f.startIndex=a.scrollIndex,G){for(;f.sizesCumulative[f.startIndex]>f.sizesCumulative[a.scrollIndex]-b;)f.startIndex--;for(c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]a.scrollIndex*f.elementSize-b;)f.startIndex--;c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=Math.min(f.startIndex+Math.ceil(H[0][L]/f.elementSize)+f.excess/2,z)}}else if(G){for(f.startIndex=0;f.sizesCumulative[f.startIndex]0&&f.startIndex--,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]').css({position:"relative","min-height":"100%","min-width":"100%"}),h.append(B),e(B)(f),f.$fillElement=B;var P={};c&&(A=b.element('
').on(d,function(a){a.preventDefault(),a.stopPropagation(),a.originalEvent&&(a=a.originalEvent),H[0].scrollLeft+=a.deltaX||-a.wheelDeltaX,H[0].scrollTop+=a.deltaY||-a.wheelDeltaY}).on("mousemove",function(a){(P.x!==a.clientX||P.y!==a.clientY)&&b.element(this).css("display","none"),P={x:a.clientX,y:a.clientY}}).css("display","none"),B.append(A)),f.startIndex=0,f.endIndex=0,H.on("scroll",function(){K?K=!1:y()&&f.$apply()}),c&&H.on(d,s),b.element(a).on("resize",t),f.$on("$destroy",function(){b.element(a).off("resize",t)}),f.$on("vsRepeatTrigger",q),f.$on("vsRepeatResize",function(){F=!0,r()});var Q,R,S;f.$watch(function(){"function"==typeof a.requestAnimationFrame?a.requestAnimationFrame(w):w()})}}}}}]),b.element(document.head).append([""].join(""))}(window,window.angular); \ No newline at end of file +!function(a,b){"use strict";var c=-1!=navigator.appVersion.indexOf("Mac"),d="undefined"!=typeof a.onwheel?"wheel":"undefined"!=typeof a.onmousewheel?"mousewheel":"DOMMouseScroll",e=document.documentElement,f=e.matches?"matches":e.matchesSelector?"matchesSelector":e.webkitMatches?"webkitMatches":e.webkitMatchesSelector?"webkitMatchesSelector":e.msMatches?"msMatches":e.msMatchesSelector?"msMatchesSelector":e.mozMatches?"mozMatches":e.mozMatchesSelector?"mozMatchesSelector":null,g=b.element.prototype.closest||function(a){for(var c=this[0].parentNode;c!==document.documentElement&&null!=c&&!c[f](a);)c=c.parentNode;return c&&c[f](a)?b.element(c):b.element()};b.module("vs-repeat",[]).directive("vsRepeat",["$compile",function(e){return{restrict:"A",scope:!0,require:"?^vsRepeat",controller:["$scope",function(a){this.$scrollParent=a.$scrollParent,this.$fillElement=a.$fillElement}],compile:function(f){var h=f.children().eq(0),i=h.attr("ng-repeat"),j=h[0].outerHTML,k=/^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(i),l=k[1],m=k[2],n=k[3],o="$vs_collection",p={vsRepeat:"elementSize",vsOffsetBefore:"offsetBefore",vsOffsetAfter:"offsetAfter",vsExcess:"excess"};return f.empty(),a.getComputedStyle&&"absolute"===a.getComputedStyle(f[0]).position||f.css("position","relative"),{pre:function(f,h,i,k){function q(a,b){if(!D||D.length<1)return f[o]=[],z=0,v(0),void(f.sizesCumulative=[0]);if(z=D.length,G){f.sizes=D.map(function(a){return a[i.vsSizeProperty]});var c=0;f.sizesCumulative=f.sizes.map(function(a){var b=c;return c+=a,b}),f.sizesCumulative.push(c)}r(),u(b)}function r(){F&&f.$$postDigest(function(){if(h[0].offsetHeight||h[0].offsetWidth)for(var a=h.children(),b=0;bb.clientWidth||b.scrollHeight>b.clientHeight)&&A.css("display","block")}function t(){"undefined"!=typeof i.vsAutoresize&&(F=!0,r(),f.$root&&!f.$root.$$phase&&f.$apply()),y()&&f.$apply()}function u(a){Q=void 0,R=void 0,y(a),v(G?f.sizesCumulative[z]:f.elementSize*z),f.$emit("vsRepeatReinitialized",f.startIndex,f.endIndex)}function v(a){if(E){if(B.css({width:f.offsetBefore+a+f.offsetAfter+"px",height:"100%"}),k&&k.$fillElement){var b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]");b&&k.$fillElement.css({width:b.scrollWidth+"px"})}}else B.css({height:f.offsetBefore+a+f.offsetAfter+"px",width:"100%"}),k&&k.$fillElement&&(b=k.$fillElement[0].parentNode.querySelector("[ng-repeat]"),b&&k.$fillElement.css({height:b.scrollHeight+"px"}))}function w(){var a=H[0][L];a!==S&&(u(),f.$root&&!f.$root.$$phase&&f.$apply()),S=a}function x(a){var b=!1;return void 0!==a&&"number"==typeof a&&(b=Math.max(a,0),H[0][N]!==b?(H[0][N]=b,K=!0):b=!1,f.$emit("vsRepeatScrolled",b)),b}function y(a){var b,c,d=!0;if(a&&void 0!==a.scrollIndex){if("undefined"!=typeof f.scrollSettings&&(f.scrollSettings.scrollIndex=a.scrollIndex),void 0!==a.scrollIndexPosition)switch("undefined"!=typeof f.scrollSettings&&(f.scrollSettings.scrollIndexPosition=a.scrollIndexPosition),b=0,typeof a.scrollIndexPosition){case"number":b=a.scrollIndexPosition+f.offsetBefore;break;case"string":switch(a.scrollIndexPosition){case"top":b=f.offsetBefore;break;case"middle":b=(H[0][L]-f.sizes[a.scrollIndex])/2;break;case"bottom":b=H[0][L]-f.sizes[a.scrollIndex]-f.offsetAfter;break;case"inview":case"inview#top":case"inview#middle":case"inview#bottom":case"inview#auto":H[0][N]<=f.sizesCumulative[a.scrollIndex]&&H[0][N]+H[0][L]-f.sizes[a.scrollIndex]>=f.sizesCumulative[a.scrollIndex]?(d=!1,b=f.sizesCumulative[a.scrollIndex]-H[0][N]):(("inview#top"===a.scrollIndexPosition||"inview"===a.scrollIndexPosition)&&(b=f.offsetBefore),"inview#bottom"===a.scrollIndexPosition&&(b=H[0][L]-f.sizes[a.scrollIndex]+f.offsetAfter),"inview#middle"===a.scrollIndexPosition&&(b=(H[0][L]-f.sizes[a.scrollIndex])/2),"inview#auto"===a.scrollIndexPosition&&(b=H[0][N]<=f.sizesCumulative[a.scrollIndex]?H[0][L]-f.sizes[a.scrollIndex]+f.offsetAfter:f.offsetBefore));break;default:console.warn("Incorrect scrollIndexPosition string value")}break;default:console.warn("Incorrect scrollIndexPosition type")}else d=!1,b=f.sizesCumulative[a.scrollIndex]-H[0][N];if(f.startIndex=a.scrollIndex,G){for(;f.sizesCumulative[f.startIndex]>f.sizesCumulative[a.scrollIndex]-b;)f.startIndex--;for(c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]a.scrollIndex*f.elementSize-b;)f.startIndex--;c=f.startIndex,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=Math.min(f.startIndex+Math.ceil(H[0][L]/f.elementSize)+f.excess/2,z)}}else if(G){for(f.startIndex=0;f.sizesCumulative[f.startIndex]0&&f.startIndex--,f.startIndex=Math.max(Math.floor(f.startIndex-f.excess/2),0),f.endIndex=f.startIndex;f.sizesCumulative[f.endIndex]').css({position:"relative","min-height":"100%","min-width":"100%"}),h.append(B),e(B)(f),f.$fillElement=B;var P={};c&&(A=b.element('
').on(d,function(a){a.preventDefault(),a.stopPropagation(),a.originalEvent&&(a=a.originalEvent),H[0].scrollLeft+=a.deltaX||-a.wheelDeltaX,H[0].scrollTop+=a.deltaY||-a.wheelDeltaY}).on("mousemove",function(a){(P.x!==a.clientX||P.y!==a.clientY)&&b.element(this).css("display","none"),P={x:a.clientX,y:a.clientY}}).css("display","none"),B.append(A)),f.startIndex=0,f.endIndex=0,H.on("scroll",function(){K?K=!1:y()&&f.$apply()}),c&&H.on(d,s),b.element(a).on("resize",t),f.$on("$destroy",function(){b.element(a).off("resize",t)}),f.$on("vsRepeatTrigger",q),f.$on("vsRepeatResize",function(){F=!0,r()});var Q,R,S;f.$watch(function(){"function"==typeof a.requestAnimationFrame?a.requestAnimationFrame(w):w()})}}}}}]),b.element(document.head).append([""].join(""))}(window,window.angular); \ No newline at end of file