From 3d4b0b4a5617e8352d5f1c298a553ed1fcab86ff Mon Sep 17 00:00:00 2001 From: dhilt Date: Sat, 19 Mar 2016 19:41:46 +0300 Subject: [PATCH 1/8] paddings precise calculation --- dist/ui-scroll-jqlite.js | 2 +- dist/ui-scroll-jqlite.min.js | 2 +- dist/ui-scroll.js | 58 +++++++++++++++++++++++++++++------- dist/ui-scroll.min.js | 4 +-- src/ui-scroll.js | 56 ++++++++++++++++++++++++++++------ 5 files changed, 99 insertions(+), 23 deletions(-) diff --git a/dist/ui-scroll-jqlite.js b/dist/ui-scroll-jqlite.js index d040d2f4..b6ea775d 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-17T12:18:01.421Z + * Version: 1.3.3 -- 2016-03-19T16:35:39.854Z * License: MIT */ diff --git a/dist/ui-scroll-jqlite.min.js b/dist/ui-scroll-jqlite.min.js index 8682f047..ef6c02b7 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-17T12:18:01.421Z + * Version: 1.3.3 -- 2016-03-19T16:35:39.854Z * 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 d4d8eefc..1ccf8aac 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-17T12:18:01.421Z + * Version: 1.3.3 -- 2016-03-19T16:35:39.854Z * License: MIT */ @@ -106,6 +106,30 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { })]; } + function Cache() { + var cache = Object.create(Array.prototype); + + angular.extend(cache, { + add: function add(item) { + for (var i = cache.length - 1; i >= 0; i--) { + if (cache[i].index === item.scope.$index) { + cache[i].height = item.element.outerHeight(); + return; + } + } + cache.push({ + index: item.scope.$index, + height: item.element.outerHeight() + }); + }, + clear: function clear() { + cache.length = 0; + } + }); + + return cache; + } + function Buffer(itemName, $scope, linker, bufferSize) { var buffer = Object.create(Array.prototype); @@ -215,7 +239,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { return buffer; } - function Viewport(buffer, element, controllers, attrs) { + function Viewport(buffer, cache, element, controllers, attrs) { var PADDING_MIN = 0.3; var PADDING_DEFAULT = 0.5; var topPadding = null; @@ -297,6 +321,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { if (buffer[i].element.offset().top - viewportOffset().top <= viewport.outerHeight() + bufferPadding()) { break; } + cache.add(buffer[i]); overage++; } @@ -319,6 +344,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { if (buffer[i].element.offset().top - viewportOffset().top + buffer[i].element.outerHeight(true) >= -1 * bufferPadding()) { break; } + cache.add(buffer[i]); overageHeight += buffer[i].element.outerHeight(true); overage++; } @@ -333,23 +359,33 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { } }, adjustPadding: function adjustPadding() { - if (!buffer.length) { + if (!buffer.length || !cache.length) { return; } - var bufferFirstEl = buffer[0].element; - var bufferLastEl = buffer[buffer.length - 1].element; - - averageItemHeight = (bufferLastEl.offset().top + bufferLastEl.outerHeight(true) - bufferFirstEl.offset().top) / buffer.length; - topPadding.height((buffer.first - buffer.minIndex) * averageItemHeight); + var topPaddingHeight = 0; + var bottomPaddingHeight = 0; + for (var i = cache.length - 1; i >= 0; i--) { + if (cache[i].index < buffer.first) { + topPaddingHeight += cache[i].height; + } + if (cache[i].index >= buffer.next) { + bottomPaddingHeight += cache[i].height; + } + } - return bottomPadding.height((buffer.maxIndex - buffer.next + 1) * averageItemHeight); + topPadding.height(topPaddingHeight); + bottomPadding.height(bottomPaddingHeight); }, syncDatasource: function syncDatasource(datasource) { if (!buffer.length) { return; } + var bufferFirstEl = buffer[0].element; + var bufferLastEl = buffer[buffer.length - 1].element; + averageItemHeight = (bufferLastEl.offset().top + bufferLastEl.outerHeight(true) - bufferFirstEl.offset().top) / buffer.length; + var delta = buffer.syncDatasource(datasource) * averageItemHeight; topPadding.height(topPadding.height() + delta); @@ -517,8 +553,9 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { var ridActual = 0; // current data revision id var pending = []; + var cache = new Cache(); var buffer = new Buffer(itemName, $scope, linker, bufferSize); - var viewport = new Viewport(buffer, element, controllers, $attr); + var viewport = new Viewport(buffer, cache, element, controllers, $attr); var adapter = new Adapter($attr, viewport, buffer, function () { dismissPendingRequests(); return adjustBuffer(ridActual); @@ -649,6 +686,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { } else { buffer.clear(); } + cache.clear(); return adjustBuffer(ridActual); } diff --git a/dist/ui-scroll.min.js b/dist/ui-scroll.min.js index 353f009a..01cd517b 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-17T12:18:01.421Z + * Version: 1.3.3 -- 2016-03-19T16:35:39.854Z * 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?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 +!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(!q)return h(b,c);if(r){var d=function(){var a=f.defer();return q.enter(b,null,c,function(){return a.resolve()}),{v:[a.promise]}}();if("object"===("undefined"==typeof d?"undefined":a(d)))return d.v}return[q.enter(b,null,c)]}function k(b){if(!q)return i(b);if(r){var c=function(){var a=f.defer();return q.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[q.leave(b.element).then(function(){return b.scope.$destroy()})]}function l(){var a=Object.create(Array.prototype);return angular.extend(a,{add:function(b){for(var c=a.length-1;c>=0;c--)if(a[c].index===b.scope.$index)return void(a[c].height=b.element.outerHeight());a.push({index:b.scope.$index,height:b.element.outerHeight()})},clear:function(){a.length=0}}),a}function m(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 n(a,b,c,d,e){function f(){return n.outerHeight()*Math.max(g,+e.padding||i)}var g=.3,i=.5,k=null,l=null,m=0,n=d[0]&&d[0].viewport?d[0].viewport:angular.element(window);n.css({"overflow-y":"auto",display:"block"});var o=n.offset()?function(){return n.offset()}:function(){return{top:0}};return angular.extend(n,{createPaddingElements:function(a){function b(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}k=new b(a),l=new b(a),c.before(k),c.after(l)},bottomDataPos:function(){var a=n[0].scrollHeight;return a=null!=a?a:n[0].document.documentElement.scrollHeight,a-l.height()},topDataPos:function(){return k.height()},bottomVisiblePos:function(){return n.scrollTop()+n.outerHeight()},topVisiblePos:function(){return n.scrollTop()},insertElement:function(a,b){return h(a,b||k)},insertElementAnimated:function(a,b){return j(a,b||k)},shouldLoadBottom:function(){return!a.eof&&n.bottomDataPos()=0&&!(a[d].element.offset().top-o().top<=n.outerHeight()+f());d--)b.add(a[d]),c++;c>0&&(a.eof=!1,a.remove(a.length-c,a.length),a.next-=c,n.adjustPadding())},shouldLoadTop:function(){return!a.bof&&n.topDataPos()>n.topVisiblePos()-f()},clipTop:function(){for(var c=0,d=0,e=0;e=-1*f());e++)b.add(a[e]),d+=a[e].element.outerHeight(!0),c++;c>0&&(k.height(k.height()+d),a.bof=!1,a.remove(0,c),a.first+=c)},adjustPadding:function(){if(a.length&&b.length){for(var c=0,d=0,e=b.length-1;e>=0;e--)b[e].index=a.next&&(d+=b[e].height);k.height(c),l.height(d)}},syncDatasource:function(b){if(a.length){var c=a[0].element,d=a[a.length-1].element;m=(d.offset().top+d.outerHeight(!0)-c.offset().top)/a.length;var e=a.syncDatasource(b)*m;k.height(k.height()+e),n.scrollTop(n.scrollTop()+e),n.adjustPadding()}},adjustScrollTop:function(a){var b=k.height()-a;b>=0?k.height(b):(k.height(0),n.scrollTop(n.scrollTop()-b))},resetTopPaddingHeight:function(){k.height(0)},resetBottomPaddingHeight:function(){l.height(0)}}),n}function o(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?K[a-1].element:void 0}var c=!1,d=[],e=[],g=[];if(K.forEach(function(a,f){switch(a.op){case"prepend":e.unshift(a);break;case"append":c=z(a,b(f))||c,a.op="none";break;case"insert":d=d.concat(L.insertElementAnimated(a.element,b(f))),a.op="none";break;case"remove":g.push(a)}}),g.forEach(function(a){return d=d.concat(K.remove(a))}),e.length){var h=0;e.forEach(function(a){c=z(a)||c,a.op="none",h+=a.element.outerHeight(!0)}),L.adjustScrollTop(h)}return K.forEach(function(a,b){return a.scope.$index=K.first+b}),d.length?f.all(d).then(function(){return L.adjustPadding(),B(a)}):(L.adjustPadding(),I.length||L.syncDatasource(G)),c}function B(a){return e(function(){return M.abCount++,A(a),L.shouldLoadBottom()?w(a,!0):L.shouldLoadTop()&&w(a,!1),I.length?void 0:M.calculateProperties()})}function C(a){return e(function(){M.abfCount++;var b=A(a);return L.shouldLoadBottom()&&b?w(a,!0):L.shouldLoadTop()&&(b||I[0])&&w(a,!1),I.shift(),I.length?D(a):(M.loading(!1),s(),M.calculateProperties())})}function D(b){return I[0]?K.length&&!L.shouldLoadBottom()?C(b):N(function(c){return b&&b!==H||a.$$destroyed?void 0:(c.length0&&(L.clipTop(),K.append(c)),K.setUpper(),C(b))}):K.length&&!L.shouldLoadTop()?C(b):O(function(c){return b&&b!==H||a.$$destroyed?void 0:(c.length0&&(K.length&&L.clipBottom(),K.prepend(c)),K.setLower(),C(b))})}function E(){d.$$phase||M.isLoading||(M.sCount++,L.shouldLoadBottom()?w(H,!0):L.shouldLoadTop()&&w(H,!1),I.length&&t())}function F(a){var b=L[0].scrollTop,c=L[0].scrollHeight-L[0].clientHeight;(0===b&&!K.bof||b===c&&!K.eof)&&a.preventDefault()}r=r||h;var G=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}(),H=0,I=[],J=new l,K=new m(j,a,r,p),L=new n(K,J,b,q,i),M=new o(i,L,K,function(){return u(),B(H)}),N=function(){return 2!==G.get.length?function(a){return G.get(K.next,p,a)}:function(a){return G.get({index:K.next,append:K.length?K[K.length-1].item:void 0,count:p},a)}}(),O=function(){return 2!==G.get.length?function(a){return G.get(K.first-p,p,a)}:function(a){return G.get({index:K.first-p,prepend:K.length?K[0].item:void 0,count:p},a)}}();if(i.adapter){var P=g(i.adapter)(a);angular.isObject(P)||(g(i.adapter).assign(a,{}),P=g(i.adapter)(a)),M=angular.extend(P,M)}r(a.$new(),function(a,b){L.createPaddingElements(a[0]),b.$destroy(),a.remove()}),M.reload=v,L.bind("mousewheel",F),a.$on("$destroy",function(){K.clear(),t(),L.unbind("mousewheel",F)}),function(){function b(a){throw new Error(a+" event is no longer supported - use applyUpdates instead")}var c=G.scope?G.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")})}(),v()}}var q=c.has&&c.has("$animate")?c.get("$animate"):null,r=1===angular.version.major&&angular.version.minor<3;return{require:["?^uiScrollViewport"],transclude:"element",priority:1e3,terminal:!0,compile:p}}])}(); \ No newline at end of file diff --git a/src/ui-scroll.js b/src/ui-scroll.js index 081d810b..0c7dec84 100644 --- a/src/ui-scroll.js +++ b/src/ui-scroll.js @@ -90,6 +90,30 @@ angular.module('ui.scroll', []) return [($animate.leave(wrapper.element)).then(() => wrapper.scope.$destroy())]; } + function Cache() { + const cache = Object.create(Array.prototype); + + angular.extend(cache, { + add(item) { + for (let i = cache.length - 1; i >= 0; i--) { + if(cache[i].index === item.scope.$index) { + cache[i].height = item.element.outerHeight(); + return; + } + } + cache.push({ + index: item.scope.$index, + height: item.element.outerHeight() + }); + }, + clear() { + cache.length = 0; + } + }); + + return cache; + } + function Buffer(itemName, $scope, linker, bufferSize) { const buffer = Object.create(Array.prototype); @@ -197,7 +221,7 @@ angular.module('ui.scroll', []) return buffer; } - function Viewport(buffer, element, controllers, attrs) { + function Viewport(buffer, cache, element, controllers, attrs) { const PADDING_MIN = 0.3; const PADDING_DEFAULT = 0.5; let topPadding = null; @@ -283,6 +307,7 @@ angular.module('ui.scroll', []) if (buffer[i].element.offset().top - viewportOffset().top <= viewport.outerHeight() + bufferPadding()) { break; } + cache.add(buffer[i]); overage++; } @@ -307,6 +332,7 @@ angular.module('ui.scroll', []) if (buffer[i].element.offset().top - viewportOffset().top + buffer[i].element.outerHeight(true) >= (-1) * bufferPadding()) { break; } + cache.add(buffer[i]); overageHeight += buffer[i].element.outerHeight(true); overage++; } @@ -322,17 +348,23 @@ angular.module('ui.scroll', []) }, adjustPadding() { - if (!buffer.length) { + if (!buffer.length || !cache.length) { return; } - const bufferFirstEl = buffer[0].element; - const bufferLastEl = buffer[buffer.length - 1].element; - - averageItemHeight = (bufferLastEl.offset().top + bufferLastEl.outerHeight(true) - bufferFirstEl.offset().top) / buffer.length; - topPadding.height((buffer.first - buffer.minIndex) * averageItemHeight); + let topPaddingHeight = 0; + let bottomPaddingHeight = 0; + for (let i = cache.length - 1; i >= 0; i--) { + if(cache[i].index < buffer.first) { + topPaddingHeight += cache[i].height; + } + if(cache[i].index >= buffer.next) { + bottomPaddingHeight += cache[i].height; + } + } - return bottomPadding.height((buffer.maxIndex - buffer.next + 1) * averageItemHeight); + topPadding.height(topPaddingHeight); + bottomPadding.height(bottomPaddingHeight); }, syncDatasource(datasource) { @@ -340,6 +372,10 @@ angular.module('ui.scroll', []) return; } + const bufferFirstEl = buffer[0].element; + const bufferLastEl = buffer[buffer.length - 1].element; + averageItemHeight = (bufferLastEl.offset().top + bufferLastEl.outerHeight(true) - bufferFirstEl.offset().top) / buffer.length; + const delta = buffer.syncDatasource(datasource) * averageItemHeight; topPadding.height(topPadding.height() + delta); @@ -503,8 +539,9 @@ angular.module('ui.scroll', []) let ridActual = 0;// current data revision id let pending = []; + let cache = new Cache(); let buffer = new Buffer(itemName, $scope, linker, bufferSize); - let viewport = new Viewport(buffer, element, controllers, $attr); + let viewport = new Viewport(buffer, cache, element, controllers, $attr); let adapter = new Adapter($attr, viewport, buffer, () => { dismissPendingRequests(); return adjustBuffer(ridActual); @@ -625,6 +662,7 @@ angular.module('ui.scroll', []) } else { buffer.clear(); } + cache.clear(); return adjustBuffer(ridActual); } From a650523e599d994e75e89b80765f626027b0403d Mon Sep 17 00:00:00 2001 From: "d.alexanov" Date: Thu, 31 Mar 2016 17:09:09 +0300 Subject: [PATCH 2/8] reload(100) demo --- demo/index.html | 5 +++++ demo/reload100/reload100.html | 36 +++++++++++++++++++++++++++++++++++ demo/reload100/reload100.js | 27 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 demo/reload100/reload100.html create mode 100644 demo/reload100/reload100.js diff --git a/demo/index.html b/demo/index.html index 82dff620..41ed88a9 100644 --- a/demo/index.html +++ b/demo/index.html @@ -54,6 +54,11 @@

    Scroller Examples

    Top visible (Adapter) +
  • + + Reload datasource to specified index + +
  • Visibility diff --git a/demo/reload100/reload100.html b/demo/reload100/reload100.html new file mode 100644 index 00000000..a66ea648 --- /dev/null +++ b/demo/reload100/reload100.html @@ -0,0 +1,36 @@ + + + + + Reload 100 + + + + + + + + +
    + + browse other examples + +

    Datasource as service

    + +
    + Here we provide an ability to reload the datasource to some specified position. +
    + +
    + - index to reload
    + +
    + +
    +
    {{item}}
    +
    + +
    + + + \ No newline at end of file diff --git a/demo/reload100/reload100.js b/demo/reload100/reload100.js new file mode 100644 index 00000000..bcae4e91 --- /dev/null +++ b/demo/reload100/reload100.js @@ -0,0 +1,27 @@ +angular.module('application', ['ui.scroll', 'ui.scroll.jqlite']) + .controller('mainController', [ + '$scope', '$log', '$timeout', function ($scope, console, $timeout) { + var datasource = {}; + + datasource.get = function (index, count, success) { + $timeout(function () { + var result = []; + for (var i = index; i <= index + count - 1; i++) { + result.push("item #" + i); + } + success(result); + }, 100); + }; + + $scope.datasource = datasource; + + $scope.doReload = function () { + if (angular.isObject($scope.adapter) && angular.isFunction($scope.adapter.reload)) { + var reloadIndex = parseInt($scope.reloadIndex, 10); + reloadIndex = isNaN(reloadIndex) ? 1 : reloadIndex; + $scope.adapter.reload(reloadIndex); + } + }; + + } + ]); From 5f504da3e6c8772a6b59957ca2bde7117c6b3c15 Mon Sep 17 00:00:00 2001 From: "d.alexanov" Date: Thu, 31 Mar 2016 17:18:25 +0300 Subject: [PATCH 3/8] reload(100) demo upd. --- demo/reload100/reload100.html | 4 ++-- demo/reload100/reload100.js | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/demo/reload100/reload100.html b/demo/reload100/reload100.html index a66ea648..be89d179 100644 --- a/demo/reload100/reload100.html +++ b/demo/reload100/reload100.html @@ -15,7 +15,7 @@ browse other examples -

    Datasource as service

    +

    Reload with parameter

    Here we provide an ability to reload the datasource to some specified position. @@ -26,7 +26,7 @@

    Datasource as service

    -
    +
    {{item}}
    diff --git a/demo/reload100/reload100.js b/demo/reload100/reload100.js index bcae4e91..db303224 100644 --- a/demo/reload100/reload100.js +++ b/demo/reload100/reload100.js @@ -23,5 +23,10 @@ angular.module('application', ['ui.scroll', 'ui.scroll.jqlite']) } }; + $scope.delay = false; + $timeout(function() { + $scope.delay = true; + }, 500); + } ]); From 913295608a8e13af78a0df7a272ed9bb5ef2111b Mon Sep 17 00:00:00 2001 From: "d.alexanov" Date: Thu, 31 Mar 2016 17:35:31 +0300 Subject: [PATCH 4/8] reload(100) demo upd.2 --- demo/reload100/reload100.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/reload100/reload100.js b/demo/reload100/reload100.js index db303224..ad9ac758 100644 --- a/demo/reload100/reload100.js +++ b/demo/reload100/reload100.js @@ -14,9 +14,10 @@ angular.module('application', ['ui.scroll', 'ui.scroll.jqlite']) }; $scope.datasource = datasource; + $scope.adapter = {}; $scope.doReload = function () { - if (angular.isObject($scope.adapter) && angular.isFunction($scope.adapter.reload)) { + if (angular.isFunction($scope.adapter.reload)) { var reloadIndex = parseInt($scope.reloadIndex, 10); reloadIndex = isNaN(reloadIndex) ? 1 : reloadIndex; $scope.adapter.reload(reloadIndex); From a7d2245f076f1dc4424f96b5e2c27133d48834aa Mon Sep 17 00:00:00 2001 From: dhilt Date: Fri, 1 Apr 2016 18:16:15 +0300 Subject: [PATCH 5/8] maxIndex/minIndex refactoring --- src/ui-scroll.js | 134 +++++++++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 50 deletions(-) diff --git a/src/ui-scroll.js b/src/ui-scroll.js index 62161882..fddb99d4 100644 --- a/src/ui-scroll.js +++ b/src/ui-scroll.js @@ -122,8 +122,10 @@ angular.module('ui.scroll', []) buffer.bof = false; buffer.first = origin; buffer.next = origin; - buffer.minIndex = Number.MAX_VALUE; - buffer.maxIndex = Number.MIN_VALUE; + buffer.minIndex = origin; + buffer.maxIndex = origin; + buffer.minIndexUser = null; + buffer.maxIndexUser = null; } angular.extend(buffer, { @@ -200,15 +202,6 @@ angular.module('ui.scroll', []) buffer.minIndex = buffer.bof ? buffer.minIndex = buffer.first : Math.min(buffer.first, buffer.minIndex); }, - syncDatasource(datasource) { - const offset = buffer.minIndex - (Math.min(buffer.minIndex, datasource.minIndex || Number.MAX_VALUE)); - - datasource.minIndex = (buffer.minIndex -= offset); - datasource.maxIndex = buffer.maxIndex = Math.max(buffer.maxIndex, datasource.maxIndex || Number.MIN_VALUE); - - return offset; - }, - // clears the buffer clear() { buffer.remove(0, buffer.length); @@ -226,7 +219,6 @@ angular.module('ui.scroll', []) const PADDING_DEFAULT = 0.5; let topPadding = null; let bottomPadding = null; - let averageItemHeight = 0; const viewport = controllers[0] && controllers[0].viewport ? controllers[0].viewport : angular.element(window); viewport.css({ @@ -246,7 +238,7 @@ angular.module('ui.scroll', []) bottomPadding = new Padding(template); element.before(topPadding); element.after(bottomPadding); - + function Padding(template) { let result; let tagName = template.localName; @@ -347,45 +339,55 @@ angular.module('ui.scroll', []) } }, - adjustPadding() { - if (!buffer.length || !cache.length) { + adjustPadding(adjustScrollTop) { + if (!buffer.length) { return; } + // percise heights calculation, items that were in buffer once let topPaddingHeight = 0; let bottomPaddingHeight = 0; - for (let i = cache.length - 1; i >= 0; i--) { - if(cache[i].index < buffer.first) { - topPaddingHeight += cache[i].height; - } - if(cache[i].index >= buffer.next) { - bottomPaddingHeight += cache[i].height; + + if(cache.length) { + for (let i = cache.length - 1; i >= 0; i--) { + if(cache[i].index < buffer.first) { + topPaddingHeight += cache[i].height; + } + if(cache[i].index >= buffer.next) { + bottomPaddingHeight += cache[i].height; + } } } - topPadding.height(topPaddingHeight); - bottomPadding.height(bottomPaddingHeight); - }, + // average heights calculation, items that have never been reached + let topPaddingHeightAdd = 0; + let bottomPaddingHeightAdd = 0; + let adjustTopPadding = buffer.minIndexUser && buffer.minIndex > buffer.minIndexUser; + let adjustBottomPadding = buffer.maxIndexUser && buffer.maxIndex < buffer.maxIndexUser; - syncDatasource(datasource) { - if (!buffer.length) { - return; + if(adjustTopPadding || adjustBottomPadding) { + let visibleItemsHeight = 0; + for (let i = buffer.length - 1; i >= 0; i--) { + visibleItemsHeight += buffer[i].element.outerHeight(true); + } + let averageItemHeight = (visibleItemsHeight + topPaddingHeight + bottomPaddingHeight) / (buffer.maxIndex - buffer.minIndex + 1); + topPaddingHeightAdd = adjustTopPadding ? (buffer.minIndex - buffer.minIndexUser) * averageItemHeight : 0; + bottomPaddingHeightAdd = adjustBottomPadding ? (buffer.maxIndexUser - buffer.maxIndex) * averageItemHeight : 0; } - const bufferFirstEl = buffer[0].element; - const bufferLastEl = buffer[buffer.length - 1].element; - averageItemHeight = (bufferLastEl.offset().top + bufferLastEl.outerHeight(true) - bufferFirstEl.offset().top) / buffer.length; + // paddings combine adjustement + let topPaddingHeightOld = topPadding.height(); + topPadding.height(topPaddingHeight + topPaddingHeightAdd); + bottomPadding.height(bottomPaddingHeight + bottomPaddingHeightAdd); - const delta = buffer.syncDatasource(datasource) * averageItemHeight; - - topPadding.height(topPadding.height() + delta); - - viewport.scrollTop(viewport.scrollTop() + delta); - - viewport.adjustPadding(); + // additional scrollTop adjustement in case of datasource.minIndex external set + if (adjustScrollTop && adjustTopPadding && topPaddingHeightAdd) { + let diff = topPadding.height() - topPaddingHeightOld; + viewport.scrollTop(viewport.scrollTop() + diff); + } }, - adjustScrollTop(height) { + adjustScrollTopAfterPrepend(height) { const paddingHeight = topPadding.height() - height; if (paddingHeight >= 0) { @@ -520,21 +522,39 @@ angular.module('ui.scroll', []) linker = linker || compileLinker; const datasource = (() => { - let _datasource = $parse(datasourceName)($scope); + let isDatasourceValid = function () { + return angular.isObject(_datasource) && angular.isFunction(_datasource.get); + }; + let _datasource = $parse(datasourceName)($scope); // try to get datasource on scope if (!isDatasourceValid()) { - _datasource = $injector.get(datasourceName); + _datasource = $injector.get(datasourceName); // try to inject datasource as service if (!isDatasourceValid()) { throw new Error(datasourceName + ' is not a valid datasource'); } } - return _datasource; + Object.defineProperty(_datasource, 'minIndex', { + set: function (value) { + this._minIndex = value; + onDatasourceMinIndexChanged(value); + }, + get: function get() { + return this._minIndex; + } + }); - function isDatasourceValid() { - // then try to inject datasource as service - return angular.isObject(_datasource) && angular.isFunction(_datasource.get); - } + Object.defineProperty(_datasource, 'maxIndex', { + set: function (value) { + this._maxIndex = value; + onDatasourceMaxIndexChanged(value); + }, + get: function get() { + return this._maxIndex; + } + }); + + return _datasource; })(); let ridActual = 0;// current data revision id @@ -547,6 +567,23 @@ angular.module('ui.scroll', []) return adjustBuffer(ridActual); }); + var onDatasourceMinIndexChanged = function(value) { + $timeout(function(){ + buffer.minIndexUser = value; + if(!pending.length) { + viewport.adjustPadding(true); + } + }); + }; + var onDatasourceMaxIndexChanged = function(value) { + $timeout(function(){ + buffer.maxIndexUser = value; + if(!pending.length) { + viewport.adjustPadding(); + } + }); + }; + const fetchNext = (() => { if (datasource.get.length !== 2) { return (success) => datasource.get(buffer.next, bufferSize, success); @@ -747,7 +784,7 @@ angular.module('ui.scroll', []) adjustedPaddingHeight += wrapper.element.outerHeight(true); }); - viewport.adjustScrollTop(adjustedPaddingHeight); + viewport.adjustScrollTopAfterPrepend(adjustedPaddingHeight); } // re-index the buffer @@ -762,9 +799,6 @@ angular.module('ui.scroll', []) }); } else { viewport.adjustPadding(); - if (!pending.length) { - viewport.syncDatasource(datasource); - } } return keepFetching; @@ -899,4 +933,4 @@ angular.module('ui.scroll', []) }; } } - ]); + ]); \ No newline at end of file From 646decc3fd8b68ae82efc50a19b63cbc1b9fd3f1 Mon Sep 17 00:00:00 2001 From: dhilt Date: Fri, 1 Apr 2016 18:17:15 +0300 Subject: [PATCH 6/8] paddings cache refactoring --- src/ui-scroll.js | 127 ++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 61 deletions(-) diff --git a/src/ui-scroll.js b/src/ui-scroll.js index fddb99d4..951b26ef 100644 --- a/src/ui-scroll.js +++ b/src/ui-scroll.js @@ -90,30 +90,6 @@ angular.module('ui.scroll', []) return [($animate.leave(wrapper.element)).then(() => wrapper.scope.$destroy())]; } - function Cache() { - const cache = Object.create(Array.prototype); - - angular.extend(cache, { - add(item) { - for (let i = cache.length - 1; i >= 0; i--) { - if(cache[i].index === item.scope.$index) { - cache[i].height = item.element.outerHeight(); - return; - } - } - cache.push({ - index: item.scope.$index, - height: item.element.outerHeight() - }); - }, - clear() { - cache.length = 0; - } - }); - - return cache; - } - function Buffer(itemName, $scope, linker, bufferSize) { const buffer = Object.create(Array.prototype); @@ -214,7 +190,7 @@ angular.module('ui.scroll', []) return buffer; } - function Viewport(buffer, cache, element, controllers, attrs) { + function Viewport(buffer, element, controllers, attrs) { const PADDING_MIN = 0.3; const PADDING_DEFAULT = 0.5; let topPadding = null; @@ -228,6 +204,52 @@ angular.module('ui.scroll', []) let viewportOffset = viewport.offset() ? () => viewport.offset() : () => ({top: 0}); + function Cache() { + const cache = Object.create(Array.prototype); + + angular.extend(cache, { + add(item) { + for (let i = cache.length - 1; i >= 0; i--) { + if(cache[i].index === item.scope.$index) { + cache[i].height = item.element.outerHeight(); + return; + } + } + cache.push({ + index: item.scope.$index, + height: item.element.outerHeight() + }); + }, + clear() { + cache.length = 0; + } + }); + + return cache; + } + + function Padding(template) { + let result; + + switch (template.tagName) { + case 'dl': + throw new Error(`ui-scroll directive does not support <${template.tagName}> as a repeating tag: ${template.outerHTML}`); + case 'tr': + let table = angular.element('
    '); + result = table.find('tr'); + break; + case 'li': + result = angular.element('
  • '); + break; + default: + result = angular.element('
    '); + } + + result.cache = new Cache(); + + return result; + } + function bufferPadding() { return viewport.outerHeight() * Math.max(PADDING_MIN, +attrs.padding || PADDING_DEFAULT); // some extra space to initiate preload } @@ -238,27 +260,6 @@ angular.module('ui.scroll', []) bottomPadding = new Padding(template); element.before(topPadding); element.after(bottomPadding); - - function Padding(template) { - let result; - let tagName = template.localName; - - switch (tagName) { - case 'dl': - throw new Error(`ui-scroll directive does not support <${tagName}> as a repeating tag: ${template.outerHTML}`); - case 'tr': - let table = angular.element('
    '); - result = table.find('tr'); - break; - case 'li': - result = angular.element('
  • '); - break; - default: - result = angular.element('
    '); - } - - return result; - } }, bottomDataPos() { @@ -299,7 +300,7 @@ angular.module('ui.scroll', []) if (buffer[i].element.offset().top - viewportOffset().top <= viewport.outerHeight() + bufferPadding()) { break; } - cache.add(buffer[i]); + bottomPadding.cache.add(buffer[i]); overage++; } @@ -324,7 +325,7 @@ angular.module('ui.scroll', []) if (buffer[i].element.offset().top - viewportOffset().top + buffer[i].element.outerHeight(true) >= (-1) * bufferPadding()) { break; } - cache.add(buffer[i]); + topPadding.cache.add(buffer[i]); overageHeight += buffer[i].element.outerHeight(true); overage++; } @@ -348,13 +349,17 @@ angular.module('ui.scroll', []) let topPaddingHeight = 0; let bottomPaddingHeight = 0; - if(cache.length) { - for (let i = cache.length - 1; i >= 0; i--) { - if(cache[i].index < buffer.first) { - topPaddingHeight += cache[i].height; + if(topPadding.cache.length) { + for (let i = topPadding.cache.length - 1; i >= 0; i--) { + if (topPadding.cache[i].index < buffer.first) { + topPaddingHeight += topPadding.cache[i].height; } - if(cache[i].index >= buffer.next) { - bottomPaddingHeight += cache[i].height; + } + } + if(bottomPadding.cache.length) { + for (let i = bottomPadding.cache.length - 1; i >= 0; i--) { + if(bottomPadding.cache[i].index >= buffer.next) { + bottomPaddingHeight += bottomPadding.cache[i].height; } } } @@ -397,11 +402,13 @@ angular.module('ui.scroll', []) viewport.scrollTop(viewport.scrollTop() - paddingHeight); } }, - resetTopPaddingHeight() { + resetTopPadding() { topPadding.height(0); + topPadding.cache.clear(); }, - resetBottomPaddingHeight() { + resetBottomPadding() { bottomPadding.height(0); + bottomPadding.cache.clear(); } }); @@ -559,9 +566,8 @@ angular.module('ui.scroll', []) let ridActual = 0;// current data revision id let pending = []; - let cache = new Cache(); let buffer = new Buffer(itemName, $scope, linker, bufferSize); - let viewport = new Viewport(buffer, cache, element, controllers, $attr); + let viewport = new Viewport(buffer, element, controllers, $attr); let adapter = new Adapter($attr, viewport, buffer, () => { dismissPendingRequests(); return adjustBuffer(ridActual); @@ -687,8 +693,8 @@ angular.module('ui.scroll', []) function reload() { dismissPendingRequests(); - viewport.resetTopPaddingHeight(); - viewport.resetBottomPaddingHeight(); + viewport.resetTopPadding(); + viewport.resetBottomPadding(); adapter.abCount = 0; adapter.abfCount = 0; @@ -699,7 +705,6 @@ angular.module('ui.scroll', []) } else { buffer.clear(); } - cache.clear(); return adjustBuffer(ridActual); } From dbe39883e3203769ba8b499ae0e70e1509704998 Mon Sep 17 00:00:00 2001 From: dhilt Date: Fri, 1 Apr 2016 18:17:56 +0300 Subject: [PATCH 7/8] artifacts --- dist/ui-scroll-jqlite.js | 6 +- dist/ui-scroll-jqlite.min.js | 2 +- dist/ui-scroll.js | 261 ++++++++++++++++++++--------------- dist/ui-scroll.min.js | 4 +- 4 files changed, 157 insertions(+), 116 deletions(-) diff --git a/dist/ui-scroll-jqlite.js b/dist/ui-scroll-jqlite.js index caf4ac9a..f6a6a4f8 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-30T12:16:26.195Z + * Version: 1.3.3 -- 2016-04-01T15:17:21.448Z * License: MIT */ @@ -231,8 +231,8 @@ angular.module('ui.scroll.jqlite', ['ui.scroll']).service('jqLiteExtras', ['$log }, - /* - The offset setter method is not implemented + /* + The offset setter method is not implemented */ offset: function offset(value) { var docElem = undefined, diff --git a/dist/ui-scroll-jqlite.min.js b/dist/ui-scroll-jqlite.min.js index d21886d9..3907f0e9 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-30T12:16:26.195Z + * Version: 1.3.3 -- 2016-04-01T15:17:21.448Z * 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 701cf95b..175bdc2e 100644 --- a/dist/ui-scroll.js +++ b/dist/ui-scroll.js @@ -1,12 +1,12 @@ -/*! - * angular-ui-scroll - * https://github.com/angular-ui/ui-scroll.git - * Version: 1.3.3 -- 2016-03-30T12:16:26.195Z - * License: MIT - */ - - - (function () { +/*! + * angular-ui-scroll + * https://github.com/angular-ui/ui-scroll.git + * Version: 1.3.3 -- 2016-04-01T15:17:21.448Z + * License: MIT + */ + + + (function () { 'use strict'; var _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === 'function' && obj.constructor === Symbol ? 'symbol' : typeof obj; }; @@ -106,30 +106,6 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { })]; } - function Cache() { - var cache = Object.create(Array.prototype); - - angular.extend(cache, { - add: function add(item) { - for (var i = cache.length - 1; i >= 0; i--) { - if (cache[i].index === item.scope.$index) { - cache[i].height = item.element.outerHeight(); - return; - } - } - cache.push({ - index: item.scope.$index, - height: item.element.outerHeight() - }); - }, - clear: function clear() { - cache.length = 0; - } - }); - - return cache; - } - function Buffer(itemName, $scope, linker, bufferSize) { var buffer = Object.create(Array.prototype); @@ -138,8 +114,10 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { buffer.bof = false; buffer.first = origin; buffer.next = origin; - buffer.minIndex = Number.MAX_VALUE; - buffer.maxIndex = Number.MIN_VALUE; + buffer.minIndex = origin; + buffer.maxIndex = origin; + buffer.minIndexUser = null; + buffer.maxIndexUser = null; } angular.extend(buffer, { @@ -217,14 +195,6 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { setLower: function setLower() { buffer.minIndex = buffer.bof ? buffer.minIndex = buffer.first : Math.min(buffer.first, buffer.minIndex); }, - syncDatasource: function syncDatasource(datasource) { - var offset = buffer.minIndex - Math.min(buffer.minIndex, datasource.minIndex || Number.MAX_VALUE); - - datasource.minIndex = buffer.minIndex -= offset; - datasource.maxIndex = buffer.maxIndex = Math.max(buffer.maxIndex, datasource.maxIndex || Number.MIN_VALUE); - - return offset; - }, // clears the buffer @@ -239,12 +209,11 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { return buffer; } - function Viewport(buffer, cache, element, controllers, attrs) { + function Viewport(buffer, element, controllers, attrs) { var PADDING_MIN = 0.3; var PADDING_DEFAULT = 0.5; var topPadding = null; var bottomPadding = null; - var averageItemHeight = 0; var viewport = controllers[0] && controllers[0].viewport ? controllers[0].viewport : angular.element(window); viewport.css({ @@ -258,6 +227,52 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { return { top: 0 }; }; + function Cache() { + var cache = Object.create(Array.prototype); + + angular.extend(cache, { + add: function add(item) { + for (var i = cache.length - 1; i >= 0; i--) { + if (cache[i].index === item.scope.$index) { + cache[i].height = item.element.outerHeight(); + return; + } + } + cache.push({ + index: item.scope.$index, + height: item.element.outerHeight() + }); + }, + clear: function clear() { + cache.length = 0; + } + }); + + return cache; + } + + function Padding(template) { + var result = undefined; + + switch (template.tagName) { + case 'dl': + throw new Error('ui-scroll directive does not support <' + template.tagName + '> as a repeating tag: ' + template.outerHTML); + case 'tr': + var table = angular.element('
    '); + result = table.find('tr'); + break; + case 'li': + result = angular.element('
  • '); + break; + default: + result = angular.element('
    '); + } + + result.cache = new Cache(); + + return result; + } + function bufferPadding() { return viewport.outerHeight() * Math.max(PADDING_MIN, +attrs.padding || PADDING_DEFAULT); // some extra space to initiate preload } @@ -268,27 +283,6 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { bottomPadding = new Padding(template); element.before(topPadding); element.after(bottomPadding); - - function Padding(template) { - var result = undefined; - var tagName = template.localName; - - switch (tagName) { - case 'dl': - throw new Error('ui-scroll directive does not support <' + tagName + '> as a repeating tag: ' + template.outerHTML); - case 'tr': - var table = angular.element('
    '); - result = table.find('tr'); - break; - case 'li': - result = angular.element('
  • '); - break; - default: - result = angular.element('
    '); - } - - return result; - } }, bottomDataPos: function bottomDataPos() { var scrollHeight = viewport[0].scrollHeight; @@ -321,7 +315,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { if (buffer[i].element.offset().top - viewportOffset().top <= viewport.outerHeight() + bufferPadding()) { break; } - cache.add(buffer[i]); + bottomPadding.cache.add(buffer[i]); overage++; } @@ -344,7 +338,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { if (buffer[i].element.offset().top - viewportOffset().top + buffer[i].element.outerHeight(true) >= -1 * bufferPadding()) { break; } - cache.add(buffer[i]); + topPadding.cache.add(buffer[i]); overageHeight += buffer[i].element.outerHeight(true); overage++; } @@ -358,43 +352,58 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { buffer.first += overage; } }, - adjustPadding: function adjustPadding() { - if (!buffer.length || !cache.length) { + adjustPadding: function adjustPadding(adjustScrollTop) { + if (!buffer.length) { return; } + // percise heights calculation, items that were in buffer once var topPaddingHeight = 0; var bottomPaddingHeight = 0; - for (var i = cache.length - 1; i >= 0; i--) { - if (cache[i].index < buffer.first) { - topPaddingHeight += cache[i].height; - } - if (cache[i].index >= buffer.next) { - bottomPaddingHeight += cache[i].height; + + if (topPadding.cache.length) { + for (var i = topPadding.cache.length - 1; i >= 0; i--) { + if (topPadding.cache[i].index < buffer.first) { + topPaddingHeight += topPadding.cache[i].height; + } } } - - topPadding.height(topPaddingHeight); - bottomPadding.height(bottomPaddingHeight); - }, - syncDatasource: function syncDatasource(datasource) { - if (!buffer.length) { - return; + if (bottomPadding.cache.length) { + for (var i = bottomPadding.cache.length - 1; i >= 0; i--) { + if (bottomPadding.cache[i].index >= buffer.next) { + bottomPaddingHeight += bottomPadding.cache[i].height; + } + } } - var bufferFirstEl = buffer[0].element; - var bufferLastEl = buffer[buffer.length - 1].element; - averageItemHeight = (bufferLastEl.offset().top + bufferLastEl.outerHeight(true) - bufferFirstEl.offset().top) / buffer.length; - - var delta = buffer.syncDatasource(datasource) * averageItemHeight; + // average heights calculation, items that have never been reached + var topPaddingHeightAdd = 0; + var bottomPaddingHeightAdd = 0; + var adjustTopPadding = buffer.minIndexUser && buffer.minIndex > buffer.minIndexUser; + var adjustBottomPadding = buffer.maxIndexUser && buffer.maxIndex < buffer.maxIndexUser; - topPadding.height(topPadding.height() + delta); + if (adjustTopPadding || adjustBottomPadding) { + var visibleItemsHeight = 0; + for (var i = buffer.length - 1; i >= 0; i--) { + visibleItemsHeight += buffer[i].element.outerHeight(true); + } + var averageItemHeight = (visibleItemsHeight + topPaddingHeight + bottomPaddingHeight) / (buffer.maxIndex - buffer.minIndex + 1); + topPaddingHeightAdd = adjustTopPadding ? (buffer.minIndex - buffer.minIndexUser) * averageItemHeight : 0; + bottomPaddingHeightAdd = adjustBottomPadding ? (buffer.maxIndexUser - buffer.maxIndex) * averageItemHeight : 0; + } - viewport.scrollTop(viewport.scrollTop() + delta); + // paddings combine adjustement + var topPaddingHeightOld = topPadding.height(); + topPadding.height(topPaddingHeight + topPaddingHeightAdd); + bottomPadding.height(bottomPaddingHeight + bottomPaddingHeightAdd); - viewport.adjustPadding(); + // additional scrollTop adjustement in case of datasource.minIndex external set + if (adjustScrollTop && adjustTopPadding && topPaddingHeightAdd) { + var diff = topPadding.height() - topPaddingHeightOld; + viewport.scrollTop(viewport.scrollTop() + diff); + } }, - adjustScrollTop: function adjustScrollTop(height) { + adjustScrollTopAfterPrepend: function adjustScrollTopAfterPrepend(height) { var paddingHeight = topPadding.height() - height; if (paddingHeight >= 0) { @@ -404,11 +413,13 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { viewport.scrollTop(viewport.scrollTop() - paddingHeight); } }, - resetTopPaddingHeight: function resetTopPaddingHeight() { + resetTopPadding: function resetTopPadding() { topPadding.height(0); + topPadding.cache.clear(); }, - resetBottomPaddingHeight: function resetBottomPaddingHeight() { + resetBottomPadding: function resetBottomPadding() { bottomPadding.height(0); + bottomPadding.cache.clear(); } }); @@ -534,33 +545,67 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { linker = linker || compileLinker; var datasource = function () { - var _datasource = $parse(datasourceName)($scope); + var isDatasourceValid = function isDatasourceValid() { + return angular.isObject(_datasource) && angular.isFunction(_datasource.get); + }; + var _datasource = $parse(datasourceName)($scope); // try to get datasource on scope if (!isDatasourceValid()) { - _datasource = $injector.get(datasourceName); + _datasource = $injector.get(datasourceName); // try to inject datasource as service if (!isDatasourceValid()) { throw new Error(datasourceName + ' is not a valid datasource'); } } - return _datasource; + Object.defineProperty(_datasource, 'minIndex', { + set: function set(value) { + this._minIndex = value; + onDatasourceMinIndexChanged(value); + }, + get: function get() { + return this._minIndex; + } + }); - function isDatasourceValid() { - // then try to inject datasource as service - return angular.isObject(_datasource) && angular.isFunction(_datasource.get); - } + Object.defineProperty(_datasource, 'maxIndex', { + set: function set(value) { + this._maxIndex = value; + onDatasourceMaxIndexChanged(value); + }, + get: function get() { + return this._maxIndex; + } + }); + + return _datasource; }(); var ridActual = 0; // current data revision id var pending = []; - var cache = new Cache(); var buffer = new Buffer(itemName, $scope, linker, bufferSize); - var viewport = new Viewport(buffer, cache, element, controllers, $attr); + var viewport = new Viewport(buffer, element, controllers, $attr); var adapter = new Adapter($attr, viewport, buffer, function () { dismissPendingRequests(); return adjustBuffer(ridActual); }); + var onDatasourceMinIndexChanged = function onDatasourceMinIndexChanged(value) { + $timeout(function () { + buffer.minIndexUser = value; + if (!pending.length) { + viewport.adjustPadding(true); + } + }); + }; + var onDatasourceMaxIndexChanged = function onDatasourceMaxIndexChanged(value) { + $timeout(function () { + buffer.maxIndexUser = value; + if (!pending.length) { + viewport.adjustPadding(); + } + }); + }; + var fetchNext = function () { if (datasource.get.length !== 2) { return function (success) { @@ -674,8 +719,8 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { function reload() { dismissPendingRequests(); - viewport.resetTopPaddingHeight(); - viewport.resetBottomPaddingHeight(); + viewport.resetTopPadding(); + viewport.resetBottomPadding(); adapter.abCount = 0; adapter.abfCount = 0; @@ -686,7 +731,6 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { } else { buffer.clear(); } - cache.clear(); return adjustBuffer(ridActual); } @@ -775,7 +819,7 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { adjustedPaddingHeight += wrapper.element.outerHeight(true); }); - viewport.adjustScrollTop(adjustedPaddingHeight); + viewport.adjustScrollTopAfterPrepend(adjustedPaddingHeight); } // re-index the buffer @@ -792,9 +836,6 @@ angular.module('ui.scroll', []).directive('uiScrollViewport', function () { }); } else { viewport.adjustPadding(); - if (!pending.length) { - viewport.syncDatasource(datasource); - } } return keepFetching; diff --git a/dist/ui-scroll.min.js b/dist/ui-scroll.min.js index 0a282868..8b5a4aa2 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-30T12:16:26.195Z + * Version: 1.3.3 -- 2016-04-01T15:17:21.448Z * 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(!q)return h(b,c);if(r){var d=function(){var a=f.defer();return q.enter(b,null,c,function(){return a.resolve()}),{v:[a.promise]}}();if("object"===("undefined"==typeof d?"undefined":a(d)))return d.v}return[q.enter(b,null,c)]}function k(b){if(!q)return i(b);if(r){var c=function(){var a=f.defer();return q.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[q.leave(b.element).then(function(){return b.scope.$destroy()})]}function l(){var a=Object.create(Array.prototype);return angular.extend(a,{add:function(b){for(var c=a.length-1;c>=0;c--)if(a[c].index===b.scope.$index)return void(a[c].height=b.element.outerHeight());a.push({index:b.scope.$index,height:b.element.outerHeight()})},clear:function(){a.length=0}}),a}function m(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 n(a,b,c,d,e){function f(){return n.outerHeight()*Math.max(g,+e.padding||i)}var g=.3,i=.5,k=null,l=null,m=0,n=d[0]&&d[0].viewport?d[0].viewport:angular.element(window);n.css({"overflow-y":"auto",display:"block"});var o=n.offset()?function(){return n.offset()}:function(){return{top:0}};return angular.extend(n,{createPaddingElements:function(a){function b(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}k=new b(a),l=new b(a),c.before(k),c.after(l)},bottomDataPos:function(){var a=n[0].scrollHeight;return a=null!=a?a:n[0].document.documentElement.scrollHeight,a-l.height()},topDataPos:function(){return k.height()},bottomVisiblePos:function(){return n.scrollTop()+n.outerHeight()},topVisiblePos:function(){return n.scrollTop()},insertElement:function(a,b){return h(a,b||k)},insertElementAnimated:function(a,b){return j(a,b||k)},shouldLoadBottom:function(){return!a.eof&&n.bottomDataPos()=0&&!(a[d].element.offset().top-o().top<=n.outerHeight()+f());d--)b.add(a[d]),c++;c>0&&(a.eof=!1,a.remove(a.length-c,a.length),a.next-=c,n.adjustPadding())},shouldLoadTop:function(){return!a.bof&&n.topDataPos()>n.topVisiblePos()-f()},clipTop:function(){for(var c=0,d=0,e=0;e=-1*f());e++)b.add(a[e]),d+=a[e].element.outerHeight(!0),c++;c>0&&(k.height(k.height()+d),a.bof=!1,a.remove(0,c),a.first+=c)},adjustPadding:function(){if(a.length&&b.length){for(var c=0,d=0,e=b.length-1;e>=0;e--)b[e].index=a.next&&(d+=b[e].height);k.height(c),l.height(d)}},syncDatasource:function(b){if(a.length){var c=a[0].element,d=a[a.length-1].element;m=(d.offset().top+d.outerHeight(!0)-c.offset().top)/a.length;var e=a.syncDatasource(b)*m;k.height(k.height()+e),n.scrollTop(n.scrollTop()+e),n.adjustPadding()}},adjustScrollTop:function(a){var b=k.height()-a;b>=0?k.height(b):(k.height(0),n.scrollTop(n.scrollTop()-b))},resetTopPaddingHeight:function(){k.height(0)},resetBottomPaddingHeight:function(){l.height(0)}}),n}function o(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?K[a-1].element:void 0}var c=!1,d=[],e=[],g=[];if(K.forEach(function(a,f){switch(a.op){case"prepend":e.unshift(a);break;case"append":c=z(a,b(f))||c,a.op="none";break;case"insert":d=d.concat(L.insertElementAnimated(a.element,b(f))),a.op="none";break;case"remove":g.push(a)}}),g.forEach(function(a){return d=d.concat(K.remove(a))}),e.length){var h=0;e.forEach(function(a){c=z(a)||c,a.op="none",h+=a.element.outerHeight(!0)}),L.adjustScrollTop(h)}return K.forEach(function(a,b){return a.scope.$index=K.first+b}),d.length?f.all(d).then(function(){return L.adjustPadding(),B(a)}):(L.adjustPadding(),I.length||L.syncDatasource(G)),c}function B(a){return e(function(){return M.abCount++,A(a),L.shouldLoadBottom()?w(a,!0):L.shouldLoadTop()&&w(a,!1),I.length?void 0:M.calculateProperties()})}function C(a){return e(function(){M.abfCount++;var b=A(a);return L.shouldLoadBottom()&&b?w(a,!0):L.shouldLoadTop()&&(b||I[0])&&w(a,!1),I.shift(),I.length?D(a):(M.loading(!1),s(),M.calculateProperties())})}function D(b){return I[0]?K.length&&!L.shouldLoadBottom()?C(b):N(function(c){return b&&b!==H||a.$$destroyed?void 0:(c.length0&&(L.clipTop(),K.append(c)),K.setUpper(),C(b))}):K.length&&!L.shouldLoadTop()?C(b):O(function(c){return b&&b!==H||a.$$destroyed?void 0:(c.length0&&(K.length&&L.clipBottom(),K.prepend(c)),K.setLower(),C(b))})}function E(){d.$$phase||M.isLoading||(M.sCount++,L.shouldLoadBottom()?w(H,!0):L.shouldLoadTop()&&w(H,!1),I.length?t():(M.calculateProperties(),a.$apply()))}function F(a){var b=L[0].scrollTop,c=L[0].scrollHeight-L[0].clientHeight;(0===b&&!K.bof||b===c&&!K.eof)&&a.preventDefault()}r=r||h;var G=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}(),H=0,I=[],J=new l,K=new m(j,a,r,p),L=new n(K,J,b,q,i),M=new o(i,L,K,function(){return u(),B(H)}),N=function(){return 2!==G.get.length?function(a){return G.get(K.next,p,a)}:function(a){return G.get({index:K.next,append:K.length?K[K.length-1].item:void 0,count:p},a)}}(),O=function(){return 2!==G.get.length?function(a){return G.get(K.first-p,p,a)}:function(a){return G.get({index:K.first-p,prepend:K.length?K[0].item:void 0,count:p},a)}}();if(i.adapter){var P=g(i.adapter)(a);angular.isObject(P)||(g(i.adapter).assign(a,{}),P=g(i.adapter)(a)),M=angular.extend(P,M)}r(a.$new(),function(a,b){L.createPaddingElements(a[0]),b.$destroy(),a.remove()}),M.reload=v,L.bind("mousewheel",F),a.$on("$destroy",function(){K.clear(),t(),L.unbind("mousewheel",F)}),function(){function b(a){throw new Error(a+" event is no longer supported - use applyUpdates instead")}var c=G.scope?G.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")})}(),v()}}var q=c.has&&c.has("$animate")?c.get("$animate"):null,r=1===angular.version.major&&angular.version.minor<3;return{require:["?^uiScrollViewport"],transclude:"element",priority:1e3,terminal:!0,compile:p}}])}(); \ 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=a,f.maxIndex=a,f.minIndexUser=null,f.maxIndexUser=null}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)},clear:function(){f.remove(0,f.length),e(arguments.length?arguments[0]:1)}}),e(1),f}function m(a,b,c,d){function e(){var a=Object.create(Array.prototype);return angular.extend(a,{add:function(b){for(var c=a.length-1;c>=0;c--)if(a[c].index===b.scope.$index)return void(a[c].height=b.element.outerHeight());a.push({index:b.scope.$index,height:b.element.outerHeight()})},clear:function(){a.length=0}}),a}function f(a){var b=void 0;switch(a.tagName){case"dl":throw new Error("ui-scroll directive does not support <"+a.tagName+"> as a repeating tag: "+a.outerHTML);case"tr":var c=angular.element("
    ");b=c.find("tr");break;case"li":b=angular.element("
  • ");break;default:b=angular.element("
    ")}return b.cache=new e,b}function g(){return n.outerHeight()*Math.max(i,+d.padding||k)}var i=.3,k=.5,l=null,m=null,n=c[0]&&c[0].viewport?c[0].viewport:angular.element(window);n.css({"overflow-y":"auto",display:"block"});var o=n.offset()?function(){return n.offset()}:function(){return{top:0}};return angular.extend(n,{createPaddingElements:function(a){l=new f(a),m=new f(a),b.before(l),b.after(m)},bottomDataPos:function(){var a=n[0].scrollHeight;return a=null!=a?a:n[0].document.documentElement.scrollHeight,a-m.height()},topDataPos:function(){return l.height()},bottomVisiblePos:function(){return n.scrollTop()+n.outerHeight()},topVisiblePos:function(){return n.scrollTop()},insertElement:function(a,b){return h(a,b||l)},insertElementAnimated:function(a,b){return j(a,b||l)},shouldLoadBottom:function(){return!a.eof&&n.bottomDataPos()=0&&!(a[c].element.offset().top-o().top<=n.outerHeight()+g());c--)m.cache.add(a[c]),b++;b>0&&(a.eof=!1,a.remove(a.length-b,a.length),a.next-=b,n.adjustPadding())},shouldLoadTop:function(){return!a.bof&&n.topDataPos()>n.topVisiblePos()-g()},clipTop:function(){for(var b=0,c=0,d=0;d=-1*g());d++)l.cache.add(a[d]),c+=a[d].element.outerHeight(!0),b++;b>0&&(l.height(l.height()+c),a.bof=!1,a.remove(0,b),a.first+=b)},adjustPadding:function(b){if(a.length){var c=0,d=0;if(l.cache.length)for(var e=l.cache.length-1;e>=0;e--)l.cache[e].index=0;e--)m.cache[e].index>=a.next&&(d+=m.cache[e].height);var f=0,g=0,h=a.minIndexUser&&a.minIndex>a.minIndexUser,i=a.maxIndexUser&&a.maxIndex=0;e--)j+=a[e].element.outerHeight(!0);var k=(j+c+d)/(a.maxIndex-a.minIndex+1);f=h?(a.minIndex-a.minIndexUser)*k:0,g=i?(a.maxIndexUser-a.maxIndex)*k:0}var o=l.height();if(l.height(c+f),m.height(d+g),b&&h&&f){var p=l.height()-o;n.scrollTop(n.scrollTop()+p)}}},adjustScrollTopAfterPrepend:function(a){var b=l.height()-a;b>=0?l.height(b):(l.height(0),n.scrollTop(n.scrollTop()-b))},resetTopPadding:function(){l.height(0),l.cache.clear()},resetBottomPadding:function(){m.height(0),m.cache.clear()}}),n}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.adjustScrollTopAfterPrepend(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(),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):N(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):O(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():(K.calculateProperties(),a.$apply()))}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(){var b=function(){return angular.isObject(d)&&angular.isFunction(d.get)},d=g(k)(a);if(!b()&&(d=c.get(k),!b()))throw new Error(k+" is not a valid datasource");return Object.defineProperty(d,"minIndex",{set:function(a){this._minIndex=a,L(a)},get:function(){return this._minIndex}}),Object.defineProperty(d,"maxIndex",{set:function(a){this._maxIndex=a,M(a)},get:function(){return this._maxIndex}}),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(a){e(function(){I.minIndexUser=a,H.length||J.adjustPadding(!0)})},M=function(a){e(function(){I.maxIndexUser=a,H.length||J.adjustPadding()})},N=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)}}(),O=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 P=g(i.adapter)(a);angular.isObject(P)||(g(i.adapter).assign(a,{}),P=g(i.adapter)(a)),K=angular.extend(P,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 From bcbfd3134ed5c06413ad9e260f56bddc8f8b415c Mon Sep 17 00:00:00 2001 From: dhilt Date: Fri, 1 Apr 2016 18:18:28 +0300 Subject: [PATCH 8/8] start/end indexes manipulating demo --- demo/index.html | 7 ++++- demo/userIndexes/userIndexes.html | 48 +++++++++++++++++++++++++++++++ demo/userIndexes/userIndexes.js | 42 +++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 demo/userIndexes/userIndexes.html create mode 100644 demo/userIndexes/userIndexes.js diff --git a/demo/index.html b/demo/index.html index 41ed88a9..4318361a 100644 --- a/demo/index.html +++ b/demo/index.html @@ -59,6 +59,11 @@

    Scroller Examples

    Reload datasource to specified index +
  • + + Manipulate start and end indexes outside the directive + +
  • Visibility @@ -109,7 +114,7 @@

    Scroller Examples

    - + \ No newline at end of file diff --git a/demo/userIndexes/userIndexes.html b/demo/userIndexes/userIndexes.html new file mode 100644 index 00000000..c5a9fbb6 --- /dev/null +++ b/demo/userIndexes/userIndexes.html @@ -0,0 +1,48 @@ + + + + + User min and max indexes + + + + + + + + + +
    + + browse other examples + +

    Set user min and max indexes

    + +
    + Here we provide an ability of external min and max indexes setting to let the viewport know how many items do we have. +
    + +
    + + minIndex value to set +
    + + maxIndex value to set +
    + +
    + +
    +
    {{item}}
    +
    + +
    + + + \ No newline at end of file diff --git a/demo/userIndexes/userIndexes.js b/demo/userIndexes/userIndexes.js new file mode 100644 index 00000000..a6eeee84 --- /dev/null +++ b/demo/userIndexes/userIndexes.js @@ -0,0 +1,42 @@ +angular.module('application', ['ui.scroll', 'ui.scroll.jqlite']) + .controller('mainController', [ + '$scope', '$log', '$timeout', function ($scope, console, $timeout) { + var datasource = {}; + + datasource.get = function (index, count, success) { + $timeout(function () { + var result = []; + for (var i = index; i <= index + count - 1; i++) { + result.push("item #" + i); + } + success(result); + }, 100); + }; + + $scope.datasource = datasource; + $scope.adapter = {}; + + $scope.setUserMinIndex = function () { + var userMinIndex = parseInt($scope.userMinIndex, 10); + if(!isNaN(userMinIndex)) + $scope.datasource.minIndex = userMinIndex; + }; + + $scope.setUserMaxIndex = function () { + var userMaxIndex = parseInt($scope.userMaxIndex, 10); + if(!isNaN(userMaxIndex)) + $scope.datasource.maxIndex = userMaxIndex; + }; + + $scope.setUserIndexes = function () { + $scope.setUserMinIndex(); + $scope.setUserMaxIndex(); + }; + + $scope.delay = false; + $timeout(function() { + $scope.delay = true; + }, 500); + + } + ]); \ No newline at end of file