diff --git a/README.md b/README.md
index 575a909a..c0ea451b 100644
--- a/README.md
+++ b/README.md
@@ -108,6 +108,13 @@ The data source object implements methods and properties to be used by the direc
**Important:** Make sure to respect the `index` and `count` parameters of the request. The array passed to the success method should have
exactly `count` elements unless it hit eof/bof
+* Properties `minIndex` and `maxIndex`
+
+ #### Description
+ As the scroller recieves the items requested by the `get` method, the value of minimum and maximum values of the item index are placed in the `minIndex` and `maxIndex` properties respectively. The values of the properties are cumulative - the value of the `minIndex` will never increase, and the value of the `maIndex` will never decrease - except the values are reset in response to a call to the adapter `reload` method. The values of the properties are used to maintain the appearance of the scroller scrollBar.
+
+ Values of the properties can be assigned programmatically. If the range of the index values is known in advance, assigneing them programmatically would improve the usability of the scrollBar.
+
###Adapter
The adapter object is an internal object created for every instance of the scroller. Properties and methods of the adapter can be used to manipulate and assess the scroller the adapter was created for. Adapter based API replaces old (undocumented) event based API introduced earlier for this purpose. The event based API is now deprecated and no longer supported.
diff --git a/demo/examples/scopeDatasource.html b/demo/examples/scopeDatasource.html
index 1f22e6af..fac46934 100644
--- a/demo/examples/scopeDatasource.html
+++ b/demo/examples/scopeDatasource.html
@@ -10,11 +10,11 @@
-
+is loading: {{loading}}; browse sample code
counters: ab {{adapter.abCount}} abf {{adapter.abfCount}} s {{adapter.sCount}}
- - *{{item}}*
+ - *{{item}}*
diff --git a/dist/ui-scroll-jqlite.js b/dist/ui-scroll-jqlite.js
index a71e562a..d040d2f4 100644
--- a/dist/ui-scroll-jqlite.js
+++ b/dist/ui-scroll-jqlite.js
@@ -1,7 +1,7 @@
/*!
* angular-ui-scroll
* https://github.com/angular-ui/ui-scroll.git
- * Version: 1.3.3 -- 2016-03-16T09:12:39.242Z
+ * Version: 1.3.3 -- 2016-03-17T12:18:01.421Z
* License: MIT
*/
diff --git a/dist/ui-scroll-jqlite.min.js b/dist/ui-scroll-jqlite.min.js
index 4973a803..8682f047 100644
--- a/dist/ui-scroll-jqlite.min.js
+++ b/dist/ui-scroll-jqlite.min.js
@@ -1,7 +1,7 @@
/*!
* angular-ui-scroll
* https://github.com/angular-ui/ui-scroll.git
- * Version: 1.3.3 -- 2016-03-16T09:12:39.242Z
+ * Version: 1.3.3 -- 2016-03-17T12:18:01.421Z
* License: MIT
*/
!function(){"use strict";var a=function(){function a(a,b){var c=[],d=!0,e=!1,f=void 0;try{for(var g,h=a[Symbol.iterator]();!(d=(g=h.next()).done)&&(c.push(g.value),!b||c.length!==b);d=!0);}catch(i){e=!0,f=i}finally{try{!d&&h["return"]&&h["return"]()}finally{if(e)throw f}}return c}return function(b,c){if(Array.isArray(b))return b;if(Symbol.iterator in Object(b))return a(b,c);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();angular.module("ui.scroll.jqlite",["ui.scroll"]).service("jqLiteExtras",["$log","$window",function(b,c){return{registerFor:function(b){function d(b,c,d){var e=b[0],f=a({top:["scrollTop","pageYOffset","scrollLeft"],left:["scrollLeft","pageXOffset","scrollTop"]}[c],3),g=f[0],h=f[1],i=f[2];return j(e)?angular.isDefined(d)?e.scrollTo(b[i].call(b),d):h in e?e[h]:e.document.documentElement[g]:(angular.isDefined(d)&&(e[g]=d),e[g])}function e(b,c){var d=void 0,e=void 0,f=void 0,h=void 0,k=void 0,l=void 0,m=void 0,n=void 0,o=void 0,p=void 0,q=void 0,r=void 0;if(j(b))return d=document.documentElement[{height:"clientHeight",width:"clientWidth"}[c]],{base:d,padding:0,border:0,margin:0};var s=a({width:[b.offsetWidth,"Left","Right"],height:[b.offsetHeight,"Top","Bottom"]}[c],3);return d=s[0],m=s[1],n=s[2],l=i(b),q=g(b,l["padding"+m])||0,r=g(b,l["padding"+n])||0,e=g(b,l["border"+m+"Width"])||0,f=g(b,l["border"+n+"Width"])||0,h=l["margin"+m],k=l["margin"+n],o=g(b,h)||0,p=g(b,k)||0,{base:d,padding:q+r,border:e+f,margin:o+p}}function f(a,b,c){var d=void 0,f=void 0,g=e(a,b);return g.base>0?{base:g.base-g.padding-g.border,outer:g.base,outerfull:g.base+g.margin}[c]:(d=i(a),f=d[b],(0>f||null===f)&&(f=a.style[b]||0),f=parseFloat(f)||0,{base:f-g.padding-g.border,outer:f,outerfull:f+g.padding+g.border+g.margin}[c])}var g,h,i,j;return h=angular.element.prototype.css,b.prototype.css=function(a,b){var c=this,d=c[0];return d&&3!==d.nodeType&&8!==d.nodeType&&d.style?h.call(c,a,b):void 0},j=function(a){return a&&a.document&&a.location&&a.alert&&a.setInterval},c.getComputedStyle?(i=function(a){return c.getComputedStyle(a,null)},g=function(a,b){return parseFloat(b)}):(i=function(a){return a.currentStyle},g=function(a,b){var c=void 0,d=void 0,e=void 0,f=void 0,g=void 0,h=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,i=new RegExp("^("+h+")(?!px)[a-z%]+$","i");return i.test(b)?(g=a.style,c=g.left,e=a.runtimeStyle,f=e&&e.left,e&&(e.left=g.left),g.left=b,d=g.pixelLeft,g.left=c,f&&(e.left=f),d):parseFloat(b)}),angular.forEach({before:function(a){var b,c,d,e,f,g,h;if(h=this,c=h[0],f=h.parent(),b=f.contents(),b[0]===c)return f.prepend(a);for(d=e=1,g=b.length-1;g>=1?g>=e:e>=g;d=g>=1?++e:--e)if(b[d]===c)return void angular.element(b[d-1]).after(a);throw new Error("invalid DOM structure "+c.outerHTML)},height:function(a){var b;return b=this,angular.isDefined(a)?(angular.isNumber(a)&&(a+="px"),h.call(b,"height",a)):f(this[0],"height","base")},outerHeight:function(a){return f(this[0],"height",a?"outerfull":"outer")},offset:function(a){var b=void 0,c=void 0,d=this,e={top:0,left:0},f=d[0],g=f&&f.ownerDocument;if(arguments.length){if(void 0===a)return d;throw new Error("offset setter method is not implemented")}return g?(b=g.documentElement,null!=f.getBoundingClientRect&&(e=f.getBoundingClientRect()),c=g.defaultView||g.parentWindow,{top:e.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:e.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):void 0},scrollTop:function(a){return d(this,"top",a)},scrollLeft:function(a){return d(this,"left",a)}},function(a,c){return b.prototype[c]?void 0:b.prototype[c]=a})}}}]).run(["$log","$window","jqLiteExtras",function(a,b,c){return b.jQuery?void 0:c.registerFor(angular.element)}])}();
\ No newline at end of file
diff --git a/dist/ui-scroll.js b/dist/ui-scroll.js
index ea16451a..d4d8eefc 100644
--- a/dist/ui-scroll.js
+++ b/dist/ui-scroll.js
@@ -1,7 +1,7 @@
/*!
* angular-ui-scroll
* https://github.com/angular-ui/ui-scroll.git
- * Version: 1.3.3 -- 2016-03-16T09:12:39.242Z
+ * Version: 1.3.3 -- 2016-03-17T12:18:01.421Z
* License: MIT
*/
@@ -586,15 +586,21 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () {
adapter.reload = reload;
// events and bindings
- viewport.bind('resize', resizeAndScrollHandler);
- viewport.bind('scroll', resizeAndScrollHandler);
+ function bindEvents() {
+ viewport.bind('resize', resizeAndScrollHandler);
+ viewport.bind('scroll', resizeAndScrollHandler);
+ }
viewport.bind('mousewheel', wheelHandler);
+ function unbindEvents() {
+ viewport.unbind('resize', resizeAndScrollHandler);
+ viewport.unbind('scroll', resizeAndScrollHandler);
+ }
+
$scope.$on('$destroy', function () {
// clear the buffer. It is necessary to remove the elements and $destroy the scopes
buffer.clear();
- viewport.unbind('resize', resizeAndScrollHandler);
- viewport.unbind('scroll', resizeAndScrollHandler);
+ unbindEvents();
viewport.unbind('mousewheel', wheelHandler);
});
@@ -634,6 +640,10 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () {
viewport.resetTopPaddingHeight();
viewport.resetBottomPaddingHeight();
+ adapter.abCount = 0;
+ adapter.abfCount = 0;
+ adapter.sCount = 0;
+
if (arguments.length) {
buffer.clear(arguments[0]);
} else {
@@ -755,6 +765,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () {
function adjustBuffer(rid) {
// We need the item bindings to be processed before we can do adjustment
return $timeout(function () {
+ adapter.abCount++;
processBufferedItems(rid);
if (viewport.shouldLoadBottom()) {
@@ -772,6 +783,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () {
function adjustBufferAfterFetch(rid) {
// We need the item bindings to be processed before we can do adjustment
return $timeout(function () {
+ adapter.abfCount++;
var keepFetching = processBufferedItems(rid);
if (viewport.shouldLoadBottom() && keepFetching) {
@@ -787,6 +799,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () {
if (!pending.length) {
adapter.loading(false);
+ bindEvents();
return adapter.calculateProperties();
}
@@ -852,7 +865,16 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () {
function resizeAndScrollHandler() {
if (!$rootScope.$$phase && !adapter.isLoading) {
- adjustBuffer();
+ adapter.sCount++;
+ if (viewport.shouldLoadBottom()) {
+ enqueueFetch(ridActual, true);
+ } else if (viewport.shouldLoadTop()) {
+ enqueueFetch(ridActual, false);
+ }
+
+ if (pending.length) {
+ unbindEvents();
+ }
}
}
diff --git a/dist/ui-scroll.min.js b/dist/ui-scroll.min.js
index 85c650df..353f009a 100644
--- a/dist/ui-scroll.min.js
+++ b/dist/ui-scroll.min.js
@@ -1,7 +1,7 @@
/*!
* angular-ui-scroll
* https://github.com/angular-ui/ui-scroll.git
- * Version: 1.3.3 -- 2016-03-16T09:12:39.242Z
+ * Version: 1.3.3 -- 2016-03-17T12:18:01.421Z
* License: MIT
*/
-!function(){"use strict";var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol?"symbol":typeof a};angular.module("ui.scroll",[]).directive("uiScrollViewport",function(){return{controller:["$scope","$element",function(a,b){return this.viewport=b,this}]}}).directive("uiScroll",["$log","$injector","$rootScope","$timeout","$q","$parse",function(b,c,d,e,f,g){function h(a,b){return b.after(a),[]}function i(a){return a.element.remove(),a.scope.$destroy(),[]}function j(b,c){if(!p)return h(b,c);if(q){var d=function(){var a=f.defer();return p.enter(b,null,c,function(){return a.resolve()}),{v:[a.promise]}}();if("object"===("undefined"==typeof d?"undefined":a(d)))return d.v}return[p.enter(b,null,c)]}function k(b){if(!p)return i(b);if(q){var c=function(){var a=f.defer();return p.leave(b.element,function(){return b.scope.$destroy(),a.resolve()}),{v:[a.promise]}}();if("object"===("undefined"==typeof c?"undefined":a(c)))return c.v}return[p.leave(b.element).then(function(){return b.scope.$destroy()})]}function l(a,b,c,d){function e(a){f.eof=!1,f.bof=!1,f.first=a,f.next=a,f.minIndex=Number.MAX_VALUE,f.maxIndex=Number.MIN_VALUE}var f=Object.create(Array.prototype);return angular.extend(f,{size:d,append:function(a){a.forEach(function(a){++f.next,f.insert("append",a)})},prepend:function(a){a.reverse().forEach(function(a){--f.first,f.insert("prepend",a)})},insert:function(d,e){var g=b.$new(),h={item:e,scope:g};if(g[a]=e,c(g,function(a){return h.element=a}),d%1===0)h.op="insert",f.splice(d,0,h);else switch(h.op=d,d){case"append":f.push(h);break;case"prepend":f.unshift(h)}},remove:function(a,b){if(angular.isNumber(a)){for(var c=a;b>c;c++)i(f[c]);return f.splice(a,b-a)}return f.splice(f.indexOf(a),1),k(a)},setUpper:function(){f.maxIndex=f.eof?f.next-1:Math.max(f.next-1,f.maxIndex)},setLower:function(){f.minIndex=f.bof?f.minIndex=f.first:Math.min(f.first,f.minIndex)},syncDatasource:function(a){var b=f.minIndex-Math.min(f.minIndex,a.minIndex||Number.MAX_VALUE);return a.minIndex=f.minIndex-=b,a.maxIndex=f.maxIndex=Math.max(f.maxIndex,a.maxIndex||Number.MIN_VALUE),b},clear:function(){f.remove(0,f.length),e(arguments.length?arguments[0]:1)}}),e(1),f}function m(a,b,c,d){function e(){return m.outerHeight()*Math.max(f,+d.padding||g)}var f=.3,g=.5,i=null,k=null,l=0,m=c[0]&&c[0].viewport?c[0].viewport:angular.element(window);m.css({"overflow-y":"auto",display:"block"});var n=m.offset()?function(){return m.offset()}:function(){return{top:0}};return angular.extend(m,{createPaddingElements:function(a){function c(a){var b=void 0,c=a.localName;switch(c){case"dl":throw new Error("ui-scroll directive does not support <"+c+"> as a repeating tag: "+a.outerHTML);case"tr":var d=angular.element("");b=d.find("tr");break;case"li":b=angular.element("");break;default:b=angular.element("")}return b}i=new c(a),k=new c(a),b.before(i),b.after(k)},bottomDataPos:function(){var a=m[0].scrollHeight;return a=null!=a?a:m[0].document.documentElement.scrollHeight,a-k.height()},topDataPos:function(){return i.height()},bottomVisiblePos:function(){return m.scrollTop()+m.outerHeight()},topVisiblePos:function(){return m.scrollTop()},insertElement:function(a,b){return h(a,b||i)},insertElementAnimated:function(a,b){return j(a,b||i)},shouldLoadBottom:function(){return!a.eof&&m.bottomDataPos()=0&&!(a[c].element.offset().top-n().top<=m.outerHeight()+e());c--)b++;b>0&&(a.eof=!1,a.remove(a.length-b,a.length),a.next-=b,m.adjustPadding())},shouldLoadTop:function(){return!a.bof&&m.topDataPos()>m.topVisiblePos()-e()},clipTop:function(){for(var b=0,c=0,d=0;d=-1*e());d++)c+=a[d].element.outerHeight(!0),b++;b>0&&(i.height(i.height()+c),a.bof=!1,a.remove(0,b),a.first+=b)},adjustPadding:function(){if(a.length){var b=a[0].element,c=a[a.length-1].element;return l=(c.offset().top+c.outerHeight(!0)-b.offset().top)/a.length,i.height((a.first-a.minIndex)*l),k.height((a.maxIndex-a.next+1)*l)}},syncDatasource:function(b){if(a.length){var c=a.syncDatasource(b)*l;i.height(i.height()+c),m.scrollTop(m.scrollTop()+c),m.adjustPadding()}},adjustScrollTop:function(a){var b=i.height()-a;b>=0?i.height(b):(i.height(0),m.scrollTop(m.scrollTop()-b))},resetTopPaddingHeight:function(){i.height(0)},resetBottomPaddingHeight:function(){k.height(0)}}),m}function n(a,b,c,e){function f(a,b){if(angular.isArray(b)){var d=void 0,e=c.indexOf(a)+1;b.reverse().forEach(function(b){b===a.item?(d=!0,e--):c.insert(e,b)}),d||(a.op="remove")}}var h=b.scope()||d,i=a.topVisible?g(a.topVisible).assign:angular.noop,j=a.topVisibleElement?g(a.topVisibleElement).assign:angular.noop,k=a.topVisibleScope?g(a.topVisibleScope).assign:angular.noop,l=a.isLoading?g(a.isLoading).assign:angular.noop;this.isLoading=!1,this.applyUpdates=function(a,b){if(angular.isFunction(a))c.slice(0).forEach(function(b){f(b,a(b.item,b.scope,b.element))});else{if(a%1!==0)throw new Error("applyUpdates - "+a+" is not a valid index");var d=a-c.first;d>=0&&d0?G[a-1].element:void 0}var c=!1,d=[],e=[],g=[];if(G.forEach(function(a,f){switch(a.op){case"prepend":e.unshift(a);break;case"append":c=w(a,b(f))||c,a.op="none";break;case"insert":d=d.concat(H.insertElementAnimated(a.element,b(f))),a.op="none";break;case"remove":g.push(a)}}),g.forEach(function(a){return d=d.concat(G.remove(a))}),e.length){var h=0;e.forEach(function(a){c=w(a)||c,a.op="none",h+=a.element.outerHeight(!0)}),H.adjustScrollTop(h)}return G.forEach(function(a,b){return a.scope.$index=G.first+b}),d.length?f.all(d).then(function(){return H.adjustPadding(),y(a)}):(H.adjustPadding(),F.length||H.syncDatasource(D)),c}function y(a){return e(function(){return x(a),H.shouldLoadBottom()?t(a,!0):H.shouldLoadTop()&&t(a,!1),F.length?void 0:I.calculateProperties()})}function z(a){return e(function(){var b=x(a);return H.shouldLoadBottom()&&b?t(a,!0):H.shouldLoadTop()&&(b||F[0])&&t(a,!1),F.shift(),F.length?A(a):(I.loading(!1),I.calculateProperties())})}function A(b){return F[0]?G.length&&!H.shouldLoadBottom()?z(b):J(function(c){return b&&b!==E||a.$$destroyed?void 0:(c.length0&&(H.clipTop(),G.append(c)),G.setUpper(),z(b))}):G.length&&!H.shouldLoadTop()?z(b):K(function(c){return b&&b!==E||a.$$destroyed?void 0:(c.length0&&(G.length&&H.clipBottom(),G.prepend(c)),G.setLower(),z(b))})}function B(){d.$$phase||I.isLoading||y()}function C(a){var b=H[0].scrollTop,c=H[0].scrollHeight-H[0].clientHeight;(0===b&&!G.bof||b===c&&!G.eof)&&a.preventDefault()}q=q||h;var D=function(){function b(){return angular.isObject(d)&&angular.isFunction(d.get)}var d=g(k)(a);if(!b()&&(d=c.get(k),!b()))throw new Error(k+" is not a valid datasource");return d}(),E=0,F=[],G=new l(j,a,q,o),H=new m(G,b,p,i),I=new n(i,H,G,function(){return r(),y(E)}),J=function(){return 2!==D.get.length?function(a){return D.get(G.next,o,a)}:function(a){return D.get({index:G.next,append:G.length?G[G.length-1].item:void 0,count:o},a)}}(),K=function(){return 2!==D.get.length?function(a){return D.get(G.first-o,o,a)}:function(a){return D.get({index:G.first-o,prepend:G.length?G[0].item:void 0,count:o},a)}}();if(i.adapter){var L=g(i.adapter)(a);angular.isObject(L)||(g(i.adapter).assign(a,{}),L=g(i.adapter)(a)),I=angular.extend(L,I)}q(a.$new(),function(a,b){H.createPaddingElements(a[0]),b.$destroy(),a.remove()}),I.reload=s,H.bind("resize",B),H.bind("scroll",B),H.bind("mousewheel",C),a.$on("$destroy",function(){G.clear(),H.unbind("resize",B),H.unbind("scroll",B),H.unbind("mousewheel",C)}),function(){function b(a){throw new Error(a+" event is no longer supported - use applyUpdates instead")}var c=D.scope?D.scope.$new():a.$new();c.$on("insert.item",function(){return b("insert")}),c.$on("update.items",function(){return b("update")}),c.$on("delete.items",function(){return b("delete")})}(),s()}}var p=c.has&&c.has("$animate")?c.get("$animate"):null,q=1===angular.version.major&&angular.version.minor<3;return{require:["?^uiScrollViewport"],transclude:"element",priority:1e3,terminal:!0,compile:o}}])}();
\ No newline at end of file
+!function(){"use strict";var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol?"symbol":typeof a};angular.module("ui.scroll",[]).directive("uiScrollViewport",function(){return{controller:["$scope","$element",function(a,b){return this.viewport=b,this}]}}).directive("uiScroll",["$log","$injector","$rootScope","$timeout","$q","$parse",function(b,c,d,e,f,g){function h(a,b){return b.after(a),[]}function i(a){return a.element.remove(),a.scope.$destroy(),[]}function j(b,c){if(!p)return h(b,c);if(q){var d=function(){var a=f.defer();return p.enter(b,null,c,function(){return a.resolve()}),{v:[a.promise]}}();if("object"===("undefined"==typeof d?"undefined":a(d)))return d.v}return[p.enter(b,null,c)]}function k(b){if(!p)return i(b);if(q){var c=function(){var a=f.defer();return p.leave(b.element,function(){return b.scope.$destroy(),a.resolve()}),{v:[a.promise]}}();if("object"===("undefined"==typeof c?"undefined":a(c)))return c.v}return[p.leave(b.element).then(function(){return b.scope.$destroy()})]}function l(a,b,c,d){function e(a){f.eof=!1,f.bof=!1,f.first=a,f.next=a,f.minIndex=Number.MAX_VALUE,f.maxIndex=Number.MIN_VALUE}var f=Object.create(Array.prototype);return angular.extend(f,{size:d,append:function(a){a.forEach(function(a){++f.next,f.insert("append",a)})},prepend:function(a){a.reverse().forEach(function(a){--f.first,f.insert("prepend",a)})},insert:function(d,e){var g=b.$new(),h={item:e,scope:g};if(g[a]=e,c(g,function(a){return h.element=a}),d%1===0)h.op="insert",f.splice(d,0,h);else switch(h.op=d,d){case"append":f.push(h);break;case"prepend":f.unshift(h)}},remove:function(a,b){if(angular.isNumber(a)){for(var c=a;b>c;c++)i(f[c]);return f.splice(a,b-a)}return f.splice(f.indexOf(a),1),k(a)},setUpper:function(){f.maxIndex=f.eof?f.next-1:Math.max(f.next-1,f.maxIndex)},setLower:function(){f.minIndex=f.bof?f.minIndex=f.first:Math.min(f.first,f.minIndex)},syncDatasource:function(a){var b=f.minIndex-Math.min(f.minIndex,a.minIndex||Number.MAX_VALUE);return a.minIndex=f.minIndex-=b,a.maxIndex=f.maxIndex=Math.max(f.maxIndex,a.maxIndex||Number.MIN_VALUE),b},clear:function(){f.remove(0,f.length),e(arguments.length?arguments[0]:1)}}),e(1),f}function m(a,b,c,d){function e(){return m.outerHeight()*Math.max(f,+d.padding||g)}var f=.3,g=.5,i=null,k=null,l=0,m=c[0]&&c[0].viewport?c[0].viewport:angular.element(window);m.css({"overflow-y":"auto",display:"block"});var n=m.offset()?function(){return m.offset()}:function(){return{top:0}};return angular.extend(m,{createPaddingElements:function(a){function c(a){var b=void 0,c=a.localName;switch(c){case"dl":throw new Error("ui-scroll directive does not support <"+c+"> as a repeating tag: "+a.outerHTML);case"tr":var d=angular.element("");b=d.find("tr");break;case"li":b=angular.element("");break;default:b=angular.element("")}return b}i=new c(a),k=new c(a),b.before(i),b.after(k)},bottomDataPos:function(){var a=m[0].scrollHeight;return a=null!=a?a:m[0].document.documentElement.scrollHeight,a-k.height()},topDataPos:function(){return i.height()},bottomVisiblePos:function(){return m.scrollTop()+m.outerHeight()},topVisiblePos:function(){return m.scrollTop()},insertElement:function(a,b){return h(a,b||i)},insertElementAnimated:function(a,b){return j(a,b||i)},shouldLoadBottom:function(){return!a.eof&&m.bottomDataPos()=0&&!(a[c].element.offset().top-n().top<=m.outerHeight()+e());c--)b++;b>0&&(a.eof=!1,a.remove(a.length-b,a.length),a.next-=b,m.adjustPadding())},shouldLoadTop:function(){return!a.bof&&m.topDataPos()>m.topVisiblePos()-e()},clipTop:function(){for(var b=0,c=0,d=0;d=-1*e());d++)c+=a[d].element.outerHeight(!0),b++;b>0&&(i.height(i.height()+c),a.bof=!1,a.remove(0,b),a.first+=b)},adjustPadding:function(){if(a.length){var b=a[0].element,c=a[a.length-1].element;return l=(c.offset().top+c.outerHeight(!0)-b.offset().top)/a.length,i.height((a.first-a.minIndex)*l),k.height((a.maxIndex-a.next+1)*l)}},syncDatasource:function(b){if(a.length){var c=a.syncDatasource(b)*l;i.height(i.height()+c),m.scrollTop(m.scrollTop()+c),m.adjustPadding()}},adjustScrollTop:function(a){var b=i.height()-a;b>=0?i.height(b):(i.height(0),m.scrollTop(m.scrollTop()-b))},resetTopPaddingHeight:function(){i.height(0)},resetBottomPaddingHeight:function(){k.height(0)}}),m}function n(a,b,c,e){function f(a,b){if(angular.isArray(b)){var d=void 0,e=c.indexOf(a)+1;b.reverse().forEach(function(b){b===a.item?(d=!0,e--):c.insert(e,b)}),d||(a.op="remove")}}var h=b.scope()||d,i=a.topVisible?g(a.topVisible).assign:angular.noop,j=a.topVisibleElement?g(a.topVisibleElement).assign:angular.noop,k=a.topVisibleScope?g(a.topVisibleScope).assign:angular.noop,l=a.isLoading?g(a.isLoading).assign:angular.noop;this.isLoading=!1,this.applyUpdates=function(a,b){if(angular.isFunction(a))c.slice(0).forEach(function(b){f(b,a(b.item,b.scope,b.element))});else{if(a%1!==0)throw new Error("applyUpdates - "+a+" is not a valid index");var d=a-c.first;d>=0&&d0?I[a-1].element:void 0}var c=!1,d=[],e=[],g=[];if(I.forEach(function(a,f){switch(a.op){case"prepend":e.unshift(a);break;case"append":c=y(a,b(f))||c,a.op="none";break;case"insert":d=d.concat(J.insertElementAnimated(a.element,b(f))),a.op="none";break;case"remove":g.push(a)}}),g.forEach(function(a){return d=d.concat(I.remove(a))}),e.length){var h=0;e.forEach(function(a){c=y(a)||c,a.op="none",h+=a.element.outerHeight(!0)}),J.adjustScrollTop(h)}return I.forEach(function(a,b){return a.scope.$index=I.first+b}),d.length?f.all(d).then(function(){return J.adjustPadding(),A(a)}):(J.adjustPadding(),H.length||J.syncDatasource(F)),c}function A(a){return e(function(){return K.abCount++,z(a),J.shouldLoadBottom()?v(a,!0):J.shouldLoadTop()&&v(a,!1),H.length?void 0:K.calculateProperties()})}function B(a){return e(function(){K.abfCount++;var b=z(a);return J.shouldLoadBottom()&&b?v(a,!0):J.shouldLoadTop()&&(b||H[0])&&v(a,!1),H.shift(),H.length?C(a):(K.loading(!1),r(),K.calculateProperties())})}function C(b){return H[0]?I.length&&!J.shouldLoadBottom()?B(b):L(function(c){return b&&b!==G||a.$$destroyed?void 0:(c.length0&&(J.clipTop(),I.append(c)),I.setUpper(),B(b))}):I.length&&!J.shouldLoadTop()?B(b):M(function(c){return b&&b!==G||a.$$destroyed?void 0:(c.length0&&(I.length&&J.clipBottom(),I.prepend(c)),I.setLower(),B(b))})}function D(){d.$$phase||K.isLoading||(K.sCount++,J.shouldLoadBottom()?v(G,!0):J.shouldLoadTop()&&v(G,!1),H.length&&s())}function E(a){var b=J[0].scrollTop,c=J[0].scrollHeight-J[0].clientHeight;(0===b&&!I.bof||b===c&&!I.eof)&&a.preventDefault()}q=q||h;var F=function(){function b(){return angular.isObject(d)&&angular.isFunction(d.get)}var d=g(k)(a);if(!b()&&(d=c.get(k),!b()))throw new Error(k+" is not a valid datasource");return d}(),G=0,H=[],I=new l(j,a,q,o),J=new m(I,b,p,i),K=new n(i,J,I,function(){return t(),A(G)}),L=function(){return 2!==F.get.length?function(a){return F.get(I.next,o,a)}:function(a){return F.get({index:I.next,append:I.length?I[I.length-1].item:void 0,count:o},a)}}(),M=function(){return 2!==F.get.length?function(a){return F.get(I.first-o,o,a)}:function(a){return F.get({index:I.first-o,prepend:I.length?I[0].item:void 0,count:o},a)}}();if(i.adapter){var N=g(i.adapter)(a);angular.isObject(N)||(g(i.adapter).assign(a,{}),N=g(i.adapter)(a)),K=angular.extend(N,K)}q(a.$new(),function(a,b){J.createPaddingElements(a[0]),b.$destroy(),a.remove()}),K.reload=u,J.bind("mousewheel",E),a.$on("$destroy",function(){I.clear(),s(),J.unbind("mousewheel",E)}),function(){function b(a){throw new Error(a+" event is no longer supported - use applyUpdates instead")}var c=F.scope?F.scope.$new():a.$new();c.$on("insert.item",function(){return b("insert")}),c.$on("update.items",function(){return b("update")}),c.$on("delete.items",function(){return b("delete")})}(),u()}}var p=c.has&&c.has("$animate")?c.get("$animate"):null,q=1===angular.version.major&&angular.version.minor<3;return{require:["?^uiScrollViewport"],transclude:"element",priority:1e3,terminal:!0,compile:o}}])}();
\ No newline at end of file
diff --git a/src/ui-scroll.js b/src/ui-scroll.js
index c18919bf..081d810b 100644
--- a/src/ui-scroll.js
+++ b/src/ui-scroll.js
@@ -568,15 +568,21 @@ angular.module('ui.scroll', [])
adapter.reload = reload;
// events and bindings
- viewport.bind('resize', resizeAndScrollHandler);
- viewport.bind('scroll', resizeAndScrollHandler);
+ function bindEvents() {
+ viewport.bind('resize', resizeAndScrollHandler);
+ viewport.bind('scroll', resizeAndScrollHandler);
+ }
viewport.bind('mousewheel', wheelHandler);
+ function unbindEvents() {
+ viewport.unbind('resize', resizeAndScrollHandler);
+ viewport.unbind('scroll', resizeAndScrollHandler);
+ }
+
$scope.$on('$destroy', () => {
// clear the buffer. It is necessary to remove the elements and $destroy the scopes
buffer.clear();
- viewport.unbind('resize', resizeAndScrollHandler);
- viewport.unbind('scroll', resizeAndScrollHandler);
+ unbindEvents();
viewport.unbind('mousewheel', wheelHandler);
});
@@ -610,6 +616,10 @@ angular.module('ui.scroll', [])
viewport.resetTopPaddingHeight();
viewport.resetBottomPaddingHeight();
+ adapter.abCount = 0;
+ adapter.abfCount = 0;
+ adapter.sCount = 0;
+
if (arguments.length) {
buffer.clear(arguments[0]);
} else {
@@ -725,6 +735,7 @@ angular.module('ui.scroll', [])
function adjustBuffer(rid) {
// We need the item bindings to be processed before we can do adjustment
return $timeout(() => {
+ adapter.abCount++;
processBufferedItems(rid);
if (viewport.shouldLoadBottom()) {
@@ -742,6 +753,7 @@ angular.module('ui.scroll', [])
function adjustBufferAfterFetch(rid) {
// We need the item bindings to be processed before we can do adjustment
return $timeout(() => {
+ adapter.abfCount++;
let keepFetching = processBufferedItems(rid);
if (viewport.shouldLoadBottom() && keepFetching) {
@@ -757,6 +769,7 @@ angular.module('ui.scroll', [])
if (!pending.length) {
adapter.loading(false);
+ bindEvents();
return adapter.calculateProperties();
}
@@ -821,7 +834,16 @@ angular.module('ui.scroll', [])
function resizeAndScrollHandler() {
if (!$rootScope.$$phase && !adapter.isLoading) {
- adjustBuffer();
+ adapter.sCount++;
+ if (viewport.shouldLoadBottom()) {
+ enqueueFetch(ridActual, true);
+ } else if (viewport.shouldLoadTop()) {
+ enqueueFetch(ridActual, false);
+ }
+
+ if (pending.length) {
+ unbindEvents();
+ }
}
}
diff --git a/test/BasicSetupSpec.js b/test/BasicSetupSpec.js
index afdf3c14..d90e6b47 100644
--- a/test/BasicSetupSpec.js
+++ b/test/BasicSetupSpec.js
@@ -14,11 +14,11 @@ describe('uiScroll', function () {
runTest(scrollSettings,
function (viewport) {
expect($.fn.bind.calls.all().length).toBe(3);
- expect($.fn.bind.calls.all()[0].args[0]).toBe('resize');
+ expect($.fn.bind.calls.all()[0].args[0]).toBe('mousewheel');
expect($.fn.bind.calls.all()[0].object[0]).toBe(viewport[0]);
- expect($.fn.bind.calls.all()[1].args[0]).toBe('scroll');
+ expect($.fn.bind.calls.all()[1].args[0]).toBe('resize');
expect($.fn.bind.calls.all()[1].object[0]).toBe(viewport[0]);
- expect($.fn.bind.calls.all()[2].args[0]).toBe('mousewheel');
+ expect($.fn.bind.calls.all()[2].args[0]).toBe('scroll');
expect($.fn.bind.calls.all()[2].object[0]).toBe(viewport[0]);
}, {
cleanupTest: function (viewport, scope, $timeout) {
diff --git a/test/BasicTestsSpec.js b/test/BasicTestsSpec.js
index f53d6079..89ef8efe 100644
--- a/test/BasicTestsSpec.js
+++ b/test/BasicTestsSpec.js
@@ -265,7 +265,6 @@ describe('uiScroll', function () {
viewport.scrollTop(400);
viewport.trigger('scroll');
- flush();
viewport.scrollTop(0);
viewport.trigger('scroll');
@@ -302,7 +301,6 @@ describe('uiScroll', function () {
viewport.scrollTop(400);
viewport.trigger('scroll');
- flush();
viewport.scrollTop(0);
viewport.trigger('scroll');
@@ -397,11 +395,10 @@ describe('uiScroll', function () {
var flush = $timeout.flush;
viewport.scrollTop(viewportHeight + itemHeight);
viewport.trigger('scroll');
- flush();
+
viewport.scrollTop(viewportHeight + itemHeight * 2);
viewport.trigger('scroll');
flush();
- expect(flush).toThrow();
expect(spy.calls.all().length).toBe(4);
@@ -434,19 +431,15 @@ describe('uiScroll', function () {
viewport.scrollTop(0); //first full, scroll to -2
viewport.trigger('scroll');
- flush();
viewport.scrollTop(0); //last full, scroll to -5, bof is reached
viewport.trigger('scroll');
flush();
- expect(flush).toThrow();
viewport.scrollTop(0); //empty, no scroll occurred (-8)
viewport.trigger('scroll');
flush();
- expect(flush).toThrow();
-
expect(spy.calls.all().length).toBe(5);
expect(spy.calls.all()[0].args[0]).toBe(1);
expect(spy.calls.all()[1].args[0]).toBe(4);
@@ -500,7 +493,7 @@ describe('uiScroll', function () {
wheelEventElement.dispatchEvent(getNewWheelEvent()); //now we are at the top but preventDefault is occurred because of bof will be reached only after next scroll trigger
expect(documentScrollBubblingCount).toBe(2); //here! the only one prevented wheel-event
- flush();
+ //flush();
wheelEventElement.dispatchEvent(getNewWheelEvent()); //preventDefault will not occurred but document will not scroll because of viewport will be scrolled
expect(documentScrollBubblingCount).toBe(3);
@@ -508,7 +501,7 @@ describe('uiScroll', function () {
viewport.scrollTop(0);
viewport.trigger('scroll'); //bof will be reached right after that
- flush();
+ //flush();
wheelEventElement.dispatchEvent(getNewWheelEvent()); //preventDefault will not occurred because of we are at the top and bof is reached
expect(documentScrollBubblingCount).toBe(4);
@@ -560,19 +553,17 @@ describe('uiScroll', function () {
expect(!!scope.container1 && !!scope.container1.adapter && !!scope.container2).toBe(true);
- scope.$watch('container2.isLoading', function(newValue, oldValue) {
- switch(++isLoadingChangeCount) {
- case 1: expect(newValue).toBe(true); expect(oldValue).toBe(true); break;
- case 2: expect(newValue).toBe(false); expect(oldValue).toBe(true); break;
- }
- expect(scope.container1.adapter.isLoading).toBe(newValue);
+ // need to review: isLoading=true can't be catched since subscribe/unsibscribe optimization
+ scope.$watch('container1.adapter.isLoading', function(newValue) {
+ isLoadingChangeCount++;
+ expect(scope.container2.isLoading).toBe(newValue);
});
viewport.scrollTop(100);
viewport.trigger('scroll');
$timeout.flush();
- expect(isLoadingChangeCount).toBe(2);
+ expect(isLoadingChangeCount).toBe(1);
}
);
});
@@ -636,16 +627,16 @@ describe('uiScroll', function () {
for(i = 0; i < limit; i++) {
viewport.scrollTop(viewport.scrollTop() + scrollDelta);
viewport.trigger('scroll');
- $timeout.flush();
}
// scroll up, return to the top
for(i = 0; i < limit; i++) {
viewport.scrollTop(viewport.scrollTop() - scrollDelta);
viewport.trigger('scroll');
- $timeout.flush();
}
+ $timeout.flush();
+
// scroll down + expectation
for(i = 0; i < limit; i++) {
viewport.scrollTop(viewport.scrollTop() + scrollDelta);
@@ -683,16 +674,16 @@ describe('uiScroll', function () {
for(i = 0; i < limit; i++) {
viewport.scrollTop(viewport.scrollTop() - scrollDelta);
viewport.trigger('scroll');
- $timeout.flush();
}
// scroll down, return to the bottom
for(i = 0; i < limit; i++) {
viewport.scrollTop(viewport.scrollTop() + scrollDelta);
viewport.trigger('scroll');
- $timeout.flush();
}
+ $timeout.flush();
+
// unstable delta passing
viewport.scrollTop(viewport.scrollTop());