From c0e11312bbe9c6f156d641707c6b22ed2e784fc1 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 7 Jan 2014 19:08:16 -0600 Subject: [PATCH] Resize after infinite scroll --- dist/css/ionic.css | 20 +- dist/js/ionic-angular.js | 53 +- dist/js/ionic.js | 588 +------------------ js/ext/angular/src/directive/ionicContent.js | 4 +- 4 files changed, 71 insertions(+), 594 deletions(-) diff --git a/dist/css/ionic.css b/dist/css/ionic.css index 00ee0f0e1e..d3b2713fc7 100644 --- a/dist/css/ionic.css +++ b/dist/css/ionic.css @@ -3,7 +3,7 @@ * Copyright 2013 Drifty Co. * http://drifty.com/ * - * Ionic, v0.9.17 + * Ionic, v{{ VERSION }} * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * @@ -2700,6 +2700,24 @@ body, .ionic-body { -moz-animation-name: refresh-spin; animation-name: refresh-spin; } +infinite-scroll .scroll-infinite { + overflow: hidden; + margin-top: -70px; + height: 60px; + position: relative; } + +.scroll-infinite-content { + position: absolute; + left: 0; + bottom: 15px; + width: 100%; + text-align: center; + font-size: 30px; + color: #666666; } + +infinite-scroll.active .scroll-infinite { + margin-top: -30px; } + .overflow-scroll { overflow-x: hidden; overflow-y: scroll; diff --git a/dist/js/ionic-angular.js b/dist/js/ionic-angular.js index 18d5d69337..a23047b823 100644 --- a/dist/js/ionic-angular.js +++ b/dist/js/ionic-angular.js @@ -2,7 +2,7 @@ * Copyright 2013 Drifty Co. * http://drifty.com/ * - * Ionic, v0.9.17 + * Ionic, v{{ VERSION }} * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * @@ -736,6 +736,8 @@ angular.module('ionic.ui.content', ['ionic.ui.service']) onScroll: '&', onScrollComplete: '&', refreshComplete: '=', + onInfiniteScroll: '=', + infiniteScrollDistance: '@', scroll: '@', hasScrollX: '@', hasScrollY: '@', @@ -828,7 +830,38 @@ angular.module('ionic.ui.content', ['ionic.ui.service']) $scope.$parent.scrollView = sv; }); - + // Check if this supports infinite scrolling and listen for scroll events + // to trigger the infinite scrolling + var infiniteScroll = $element.find('infinite-scroll'); + var infiniteStarted = false; + if(infiniteScroll) { + // Parse infinite scroll distance + var distance = attr.infiniteScrollDistance || '1%'; + var maxScroll; + if(distance.indexOf('%')) { + // It's a multiplier + maxScroll = function() { + return sv.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 ); + }; + } else { + // It's a pixel value + maxScroll = function() { + return sv.getScrollMax().top - parseInt(distance, 10); + }; + } + $element.bind('scroll', function(e) { + if( sv && !infiniteStarted && (sv.getValues().top > maxScroll() ) ) { + infiniteStarted = true; + infiniteScroll.addClass('active'); + var cb = function() { + sv.resize(); + infiniteStarted = false; + infiniteScroll.removeClass('active'); + }; + $scope.$apply(angular.bind($scope, $scope.onInfiniteScroll, cb)); + } + }); + } } // if padding attribute is true, then add padding if it wasn't added to the .scroll @@ -858,8 +891,15 @@ angular.module('ionic.ui.content', ['ionic.ui.service']) transclude: true, template: '
' }; -}); +}) +.directive('infiniteScroll', function() { + return { + restrict: 'E', + replace: false, + template: '
' + }; +}); })(); ; @@ -868,7 +908,7 @@ angular.module('ionic.ui.content', ['ionic.ui.service']) angular.module('ionic.ui.list', ['ngAnimate']) -.directive('item', ['$timeout', function($timeout) { +.directive('item', ['$timeout', '$parse', function($timeout, $parse) { return { restrict: 'E', require: '?^list', @@ -887,7 +927,7 @@ angular.module('ionic.ui.list', ['ngAnimate']) reorderIcon: '@' }, - template: '
\ + template: '
\
\ \
\ @@ -912,7 +952,8 @@ angular.module('ionic.ui.list', ['ngAnimate']) // Set this item's class, first from the item directive attr, and then the list attr if item not set $scope.itemClass = $scope.itemType || $parentScope.itemType; - + var getter = $parse( $scope.itemClass ); + $scope.parsedClass = angular.bind( $scope, getter, $scope.$parent ); // Decide if this item can do stuff, and follow a certain priority // depending on where the value comes from if(($attr.canDelete ? $scope.canDelete : $parentScope.canDelete) !== "false") { diff --git a/dist/js/ionic.js b/dist/js/ionic.js index c3111ac4bd..7826e26588 100644 --- a/dist/js/ionic.js +++ b/dist/js/ionic.js @@ -2,7 +2,7 @@ * Copyright 2013 Drifty Co. * http://drifty.com/ * - * Ionic, v0.9.17 + * Ionic, v{{ VERSION }} * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * @@ -16,7 +16,7 @@ window.ionic = { controllers: {}, views: {}, - version: '0.9.17' + version: '{{ VERSION }}' };; (function(ionic) { @@ -4933,590 +4933,6 @@ ionic.views.Scroll = ionic.views.View.inherit({ } }); -})(ionic); -; -/* - * Adapted from Swipe.js 2.0 - * - * Brad Birdsall - * Copyright 2013, MIT License - * -*/ - -(function(ionic) { -'use strict'; - -ionic.views.Slider = ionic.views.View.inherit({ - initialize: function (options) { - // utilities - var noop = function() {}; // simple no operation function - var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution - - // check browser capabilities - var browser = { - addEventListener: !!window.addEventListener, - touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, - transitions: (function(temp) { - var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition']; - for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true; - return false; - })(document.createElement('swipe')) - }; - - - var container = options.el; - - // quit if no root element - if (!container) return; - var element = container.children[0]; - var slides, slidePos, width, length; - options = options || {}; - var index = parseInt(options.startSlide, 10) || 0; - var speed = options.speed || 300; - options.continuous = options.continuous !== undefined ? options.continuous : true; - - function setup() { - - // cache slides - slides = element.children; - length = slides.length; - - // set continuous to false if only one slide - if (slides.length < 2) options.continuous = false; - - //special case if two slides - if (browser.transitions && options.continuous && slides.length < 3) { - element.appendChild(slides[0].cloneNode(true)); - element.appendChild(element.children[1].cloneNode(true)); - slides = element.children; - } - - // create an array to store current positions of each slide - slidePos = new Array(slides.length); - - // determine width of each slide - width = container.getBoundingClientRect().width || container.offsetWidth; - - element.style.width = (slides.length * width) + 'px'; - - // stack elements - var pos = slides.length; - while(pos--) { - - var slide = slides[pos]; - - slide.style.width = width + 'px'; - slide.setAttribute('data-index', pos); - - if (browser.transitions) { - slide.style.left = (pos * -width) + 'px'; - move(pos, index > pos ? -width : (index < pos ? width : 0), 0); - } - - } - - // reposition elements before and after index - if (options.continuous && browser.transitions) { - move(circle(index-1), -width, 0); - move(circle(index+1), width, 0); - } - - if (!browser.transitions) element.style.left = (index * -width) + 'px'; - - container.style.visibility = 'visible'; - - options.slidesChanged && options.slidesChanged(); - } - - function prev() { - - if (options.continuous) slide(index-1); - else if (index) slide(index-1); - - } - - function next() { - - if (options.continuous) slide(index+1); - else if (index < slides.length - 1) slide(index+1); - - } - - function circle(index) { - - // a simple positive modulo using slides.length - return (slides.length + (index % slides.length)) % slides.length; - - } - - function slide(to, slideSpeed) { - - // do nothing if already on requested slide - if (index == to) return; - - if (browser.transitions) { - - var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward - - // get the actual position of the slide - if (options.continuous) { - var natural_direction = direction; - direction = -slidePos[circle(to)] / width; - - // if going forward but to < index, use to = slides.length + to - // if going backward but to > index, use to = -slides.length + to - if (direction !== natural_direction) to = -direction * slides.length + to; - - } - - var diff = Math.abs(index-to) - 1; - - // move all the slides between index and to in the right direction - while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0); - - to = circle(to); - - move(index, width * direction, slideSpeed || speed); - move(to, 0, slideSpeed || speed); - - if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place - - } else { - - to = circle(to); - animate(index * -width, to * -width, slideSpeed || speed); - //no fallback for a circular continuous if the browser does not accept transitions - } - - index = to; - offloadFn(options.callback && options.callback(index, slides[index])); - } - - function move(index, dist, speed) { - - translate(index, dist, speed); - slidePos[index] = dist; - - } - - function translate(index, dist, speed) { - - var slide = slides[index]; - var style = slide && slide.style; - - if (!style) return; - - style.webkitTransitionDuration = - style.MozTransitionDuration = - style.msTransitionDuration = - style.OTransitionDuration = - style.transitionDuration = speed + 'ms'; - - style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)'; - style.msTransform = - style.MozTransform = - style.OTransform = 'translateX(' + dist + 'px)'; - - } - - function animate(from, to, speed) { - - // if not an animation, just reposition - if (!speed) { - - element.style.left = to + 'px'; - return; - - } - - var start = +new Date; - - var timer = setInterval(function() { - - var timeElap = +new Date - start; - - if (timeElap > speed) { - - element.style.left = to + 'px'; - - if (delay) begin(); - - options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); - - clearInterval(timer); - return; - - } - - element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px'; - - }, 4); - - } - - // setup auto slideshow - var delay = options.auto || 0; - var interval; - - function begin() { - - interval = setTimeout(next, delay); - - } - - function stop() { - - delay = 0; - clearTimeout(interval); - - } - - - // setup initial vars - var start = {}; - var delta = {}; - var isScrolling; - - // setup event capturing - var events = { - - handleEvent: function(event) { - if(event.type == 'mousedown' || event.type == 'mouseup' || event.type == 'mousemove') { - event.touches = [{ - pageX: event.pageX, - pageY: event.pageY - }]; - } - - switch (event.type) { - case 'mousedown': this.start(event); break; - case 'touchstart': this.start(event); break; - case 'touchmove': this.move(event); break; - case 'mousemove': this.move(event); break; - case 'touchend': offloadFn(this.end(event)); break; - case 'mouseup': offloadFn(this.end(event)); break; - case 'webkitTransitionEnd': - case 'msTransitionEnd': - case 'oTransitionEnd': - case 'otransitionend': - case 'transitionend': offloadFn(this.transitionEnd(event)); break; - case 'resize': offloadFn(setup); break; - } - - if (options.stopPropagation) event.stopPropagation(); - - }, - start: function(event) { - - var touches = event.touches[0]; - - // measure start values - start = { - - // get initial touch coords - x: touches.pageX, - y: touches.pageY, - - // store time to determine touch duration - time: +new Date - - }; - - // used for testing first move event - isScrolling = undefined; - - // reset delta and end measurements - delta = {}; - - // attach touchmove and touchend listeners - if(browser.touch) { - element.addEventListener('touchmove', this, false); - element.addEventListener('touchend', this, false); - } else { - element.addEventListener('mousemove', this, false); - element.addEventListener('mouseup', this, false); - document.addEventListener('mouseup', this, false); - } - }, - move: function(event) { - - // ensure swiping with one touch and not pinching - if ( event.touches.length > 1 || event.scale && event.scale !== 1) return - - if (options.disableScroll) event.preventDefault(); - - var touches = event.touches[0]; - - // measure change in x and y - delta = { - x: touches.pageX - start.x, - y: touches.pageY - start.y - } - - // determine if scrolling test has run - one time test - if ( typeof isScrolling == 'undefined') { - isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) ); - } - - // if user is not trying to scroll vertically - if (!isScrolling) { - - // prevent native scrolling - event.preventDefault(); - - // stop slideshow - stop(); - - // increase resistance if first or last slide - if (options.continuous) { // we don't add resistance at the end - - translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0); - translate(index, delta.x + slidePos[index], 0); - translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0); - - } else { - - delta.x = - delta.x / - ( (!index && delta.x > 0 // if first slide and sliding left - || index == slides.length - 1 // or if last slide and sliding right - && delta.x < 0 // and if sliding at all - ) ? - ( Math.abs(delta.x) / width + 1 ) // determine resistance level - : 1 ); // no resistance if false - - // translate 1:1 - translate(index-1, delta.x + slidePos[index-1], 0); - translate(index, delta.x + slidePos[index], 0); - translate(index+1, delta.x + slidePos[index+1], 0); - } - - } - - }, - end: function(event) { - - // measure duration - var duration = +new Date - start.time; - - // determine if slide attempt triggers next/prev slide - var isValidSlide = - Number(duration) < 250 // if slide duration is less than 250ms - && Math.abs(delta.x) > 20 // and if slide amt is greater than 20px - || Math.abs(delta.x) > width/2; // or if slide amt is greater than half the width - - // determine if slide attempt is past start and end - var isPastBounds = - !index && delta.x > 0 // if first slide and slide amt is greater than 0 - || index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0 - - if (options.continuous) isPastBounds = false; - - // determine direction of swipe (true:right, false:left) - var direction = delta.x < 0; - - // if not scrolling vertically - if (!isScrolling) { - - if (isValidSlide && !isPastBounds) { - - if (direction) { - - if (options.continuous) { // we need to get the next in this direction in place - - move(circle(index-1), -width, 0); - move(circle(index+2), width, 0); - - } else { - move(index-1, -width, 0); - } - - move(index, slidePos[index]-width, speed); - move(circle(index+1), slidePos[circle(index+1)]-width, speed); - index = circle(index+1); - - } else { - if (options.continuous) { // we need to get the next in this direction in place - - move(circle(index+1), width, 0); - move(circle(index-2), -width, 0); - - } else { - move(index+1, width, 0); - } - - move(index, slidePos[index]+width, speed); - move(circle(index-1), slidePos[circle(index-1)]+width, speed); - index = circle(index-1); - - } - - options.callback && options.callback(index, slides[index]); - - } else { - - if (options.continuous) { - - move(circle(index-1), -width, speed); - move(index, 0, speed); - move(circle(index+1), width, speed); - - } else { - - move(index-1, -width, speed); - move(index, 0, speed); - move(index+1, width, speed); - } - - } - - } - - // kill touchmove and touchend event listeners until touchstart called again - if(browser.touch) { - element.removeEventListener('touchmove', events, false) - element.removeEventListener('touchend', events, false) - } else { - element.removeEventListener('mousemove', events, false) - element.removeEventListener('mouseup', events, false) - document.removeEventListener('mouseup', events, false); - } - - }, - transitionEnd: function(event) { - - if (parseInt(event.target.getAttribute('data-index'), 10) == index) { - - if (delay) begin(); - - options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); - - } - - } - - } - - // Public API - this.setup = function() { - setup(); - }; - - this.slide = function(to, speed) { - // cancel slideshow - stop(); - - slide(to, speed); - }; - - this.prev = function() { - // cancel slideshow - stop(); - - prev(); - }; - - this.next = function() { - // cancel slideshow - stop(); - - next(); - }; - - this.stop = function() { - // cancel slideshow - stop(); - }; - - this.getPos = function() { - // return current index position - return index; - }; - - this.getNumSlides = function() { - // return total number of slides - return length; - }; - - this.kill = function() { - // cancel slideshow - stop(); - - // reset element - element.style.width = ''; - element.style.left = ''; - - // reset slides - var pos = slides.length; - while(pos--) { - - var slide = slides[pos]; - slide.style.width = ''; - slide.style.left = ''; - - if (browser.transitions) translate(pos, 0, 0); - - } - - // removed event listeners - if (browser.addEventListener) { - - // remove current event listeners - element.removeEventListener('touchstart', events, false); - element.removeEventListener('webkitTransitionEnd', events, false); - element.removeEventListener('msTransitionEnd', events, false); - element.removeEventListener('oTransitionEnd', events, false); - element.removeEventListener('otransitionend', events, false); - element.removeEventListener('transitionend', events, false); - window.removeEventListener('resize', events, false); - - } - else { - - window.onresize = null; - - } - }; - - this.load = function() { - // trigger setup - setup(); - - // start auto slideshow if applicable - if (delay) begin(); - - - // add event listeners - if (browser.addEventListener) { - - // set touchstart event on element - if (browser.touch) { - element.addEventListener('touchstart', events, false); - } else { - element.addEventListener('mousedown', events, false); - } - - if (browser.transitions) { - element.addEventListener('webkitTransitionEnd', events, false); - element.addEventListener('msTransitionEnd', events, false); - element.addEventListener('oTransitionEnd', events, false); - element.addEventListener('otransitionend', events, false); - element.addEventListener('transitionend', events, false); - } - - // set resize event on window - window.addEventListener('resize', events, false); - - } else { - - window.onresize = function () { setup() }; // to play nice with old IE - - } - } - - } -}); - })(ionic); ; (function(ionic) { diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js index 9511521d38..6b12782c8e 100644 --- a/js/ext/angular/src/directive/ionicContent.js +++ b/js/ext/angular/src/directive/ionicContent.js @@ -124,7 +124,8 @@ angular.module('ionic.ui.content', ['ionic.ui.service']) $scope.$parent.scrollView = sv; }); - // Infinite scroll + // Check if this supports infinite scrolling and listen for scroll events + // to trigger the infinite scrolling var infiniteScroll = $element.find('infinite-scroll'); var infiniteStarted = false; if(infiniteScroll) { @@ -147,6 +148,7 @@ angular.module('ionic.ui.content', ['ionic.ui.service']) infiniteStarted = true; infiniteScroll.addClass('active'); var cb = function() { + sv.resize(); infiniteStarted = false; infiniteScroll.removeClass('active'); };