From 727cd0ca41517021e96c28ce9aeb960995520f16 Mon Sep 17 00:00:00 2001 From: Michael Feingold Date: Mon, 14 Mar 2016 17:51:22 -0500 Subject: [PATCH 1/6] adjustBuffer counters --- demo/examples/scopeDatasource.html | 4 ++-- src/ui-scroll.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) 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

+

is loading: {{loading}};       browse sample code
counters: ab {{adapter.abCount}} abf {{adapter.abfCount}} s {{adapter.sCount}}

diff --git a/src/ui-scroll.js b/src/ui-scroll.js index d3a24791..9e9fc01c 100644 --- a/src/ui-scroll.js +++ b/src/ui-scroll.js @@ -610,6 +610,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 +729,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 +747,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) { @@ -821,6 +827,7 @@ angular.module('ui.scroll', []) function resizeAndScrollHandler() { if (!$rootScope.$$phase && !adapter.isLoading) { + adapter.sCount++; adjustBuffer(); } } From 7c839c20f4ad3c73c5dc539131a3741c34c2c4b5 Mon Sep 17 00:00:00 2001 From: Michael Feingold Date: Mon, 14 Mar 2016 19:01:40 -0500 Subject: [PATCH 2/6] A fix for the performance problem? --- src/ui-scroll.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/ui-scroll.js b/src/ui-scroll.js index 9e9fc01c..8fc9bd87 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); }); @@ -738,7 +744,9 @@ angular.module('ui.scroll', []) enqueueFetch(rid, false); } - if (!pending.length) { + if (pending.length) { +// unbindEvents(); + } else { return adapter.calculateProperties(); } }); @@ -763,6 +771,7 @@ angular.module('ui.scroll', []) if (!pending.length) { adapter.loading(false); + bindEvents(); return adapter.calculateProperties(); } @@ -828,7 +837,15 @@ angular.module('ui.scroll', []) function resizeAndScrollHandler() { if (!$rootScope.$$phase && !adapter.isLoading) { adapter.sCount++; - adjustBuffer(); + if (viewport.shouldLoadBottom()) { + enqueueFetch(ridActual, true); + } else if (viewport.shouldLoadTop()) { + enqueueFetch(ridActual, false); + } + + if (pending.length) { + unbindEvents(); + } } } From 1500958a8c64656d49befee3f81cd8f1a1ddeb10 Mon Sep 17 00:00:00 2001 From: Michael Feingold Date: Wed, 16 Mar 2016 14:22:33 -0500 Subject: [PATCH 3/6] fixed linter complains --- src/ui-scroll.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ui-scroll.js b/src/ui-scroll.js index 8fc9bd87..a6a8dd51 100644 --- a/src/ui-scroll.js +++ b/src/ui-scroll.js @@ -744,9 +744,7 @@ angular.module('ui.scroll', []) enqueueFetch(rid, false); } - if (pending.length) { -// unbindEvents(); - } else { + if (!pending.length) { return adapter.calculateProperties(); } }); From 3ff93b10829c84552ff8f861db90b4eaa8c2f97c Mon Sep 17 00:00:00 2001 From: Michael Feingold Date: Wed, 16 Mar 2016 14:45:33 -0500 Subject: [PATCH 4/6] fixed 4 broken tests --- test/BasicSetupSpec.js | 6 +++--- test/BasicTestsSpec.js | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) 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..a6ab6a8f 100644 --- a/test/BasicTestsSpec.js +++ b/test/BasicTestsSpec.js @@ -400,8 +400,8 @@ describe('uiScroll', function () { flush(); viewport.scrollTop(viewportHeight + itemHeight * 2); viewport.trigger('scroll'); - flush(); - expect(flush).toThrow(); + //flush(); + //expect(flush).toThrow(); expect(spy.calls.all().length).toBe(4); @@ -443,9 +443,9 @@ describe('uiScroll', function () { expect(flush).toThrow(); viewport.scrollTop(0); //empty, no scroll occurred (-8) viewport.trigger('scroll'); - flush(); - expect(flush).toThrow(); + //flush(); + //expect(flush).toThrow(); expect(spy.calls.all().length).toBe(5); expect(spy.calls.all()[0].args[0]).toBe(1); @@ -500,7 +500,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 +508,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); From 310d023798a003a944bb703c6ecf9bf5c8076b8b Mon Sep 17 00:00:00 2001 From: dhilt Date: Thu, 17 Mar 2016 15:20:20 +0300 Subject: [PATCH 5/6] tests fix + artifacts build --- dist/ui-scroll-jqlite.js | 2 +- dist/ui-scroll-jqlite.min.js | 2 +- dist/ui-scroll.js | 34 ++++++++++++++++++++++++++++------ dist/ui-scroll.min.js | 4 ++-- test/BasicTestsSpec.js | 33 ++++++++++++--------------------- 5 files changed, 44 insertions(+), 31 deletions(-) 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/test/BasicTestsSpec.js b/test/BasicTestsSpec.js index a6ab6a8f..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(); + flush(); expect(spy.calls.all().length).toBe(4); @@ -434,18 +431,14 @@ 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(); + flush(); expect(spy.calls.all().length).toBe(5); expect(spy.calls.all()[0].args[0]).toBe(1); @@ -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()); From 3c3107e173df9c9a66b0a0a8c854c04019c0e2bf Mon Sep 17 00:00:00 2001 From: Michael Feingold Date: Fri, 18 Mar 2016 12:59:54 -0500 Subject: [PATCH 6/6] Update README.md Min/Max Index description --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) 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.