diff --git a/Gruntfile.js b/Gruntfile.js index 66754101dc..ce7f4170d8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -30,7 +30,7 @@ module.exports = function(grunt) { 'js/views/navBarView.js', 'js/views/popupView.js', 'js/views/sideMenuView.js', - 'js/views/slideBoxView.js', + 'js/views/sliderView.js', 'js/views/tabBarView.js', 'js/views/toggleView.js', diff --git a/dist/css/ionic.css b/dist/css/ionic.css index c8ed992174..ca4f0e701d 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.14 + * Ionic, v0.9.17 * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * @@ -4408,44 +4408,40 @@ button.item-button-right:after { * Slide Box * -------------------------------------------------- */ -.slide-box { +.slider { position: relative; overflow: hidden; + visibility: hidden; background-color: #000; } -.slide-box-slides { +.slider-slides { position: relative; white-space: nowrap; - font-size: 0; - -webkit-transition: -webkit-transform 0 ease-in-out; - -moz-transition: -moz-transform 0 ease-in-out; - transition: transform 0 ease-in-out; } + font-size: 0; } -.slide-box-animating { - -webkit-transition-duration: 0.2s; - -moz-transition-duration: 0.2s; - transition-duration: 0.2s; } - -.slide { - display: inline-block; +.slider-slide { + display: block; + position: relative; width: 100%; - height: 100%; + float: left; vertical-align: top; } - .slide img { + .slider-slide img { width: 100%; } -.slide-box-pager { +.slider-pager { position: absolute; bottom: 20px; width: 100%; - text-align: center; } - .slide-box-pager > * { + text-align: center; + z-index: 1; } + .slider-pager .slider-pager-page { display: inline-block; - margin: 0px 5px; + margin: 0px 3px; + width: 15px; color: #fff; text-decoration: none; opacity: 0.3; } - .slide-box-pager > *.active { + .slider-pager .slider-pager-page.active { opacity: 1; -webkit-transition: opacity 0.4s ease-in; -moz-transition: opacity 0.4s ease-in; diff --git a/dist/js/ionic-angular.js b/dist/js/ionic-angular.js index 485cc85271..0e6854c178 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.14 + * Ionic, v0.9.17 * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * @@ -1811,32 +1811,38 @@ angular.module('ionic.ui.slideBox', []) * some side menu stuff on the current scope. */ -.directive('slideBox', ['$compile', function($compile) { +.directive('slideBox', ['$timeout', '$compile', function($timeout, $compile) { return { restrict: 'E', replace: true, transclude: true, scope: {}, controller: ['$scope', '$element', function($scope, $element) { - $scope.slides = []; - this.slideAdded = function() { - $scope.slides.push({}); - }; + var _this = this; - angular.extend(this, ionic.views.SlideBox.prototype); - - ionic.views.SlideBox.call(this, { + var slider = new ionic.views.Slider({ el: $element[0], - slideChanged: function(slideIndex) { + slidesChanged: function() { + $scope.currentSlide = slider.getPos(); + $timeout(function() {}); + }, + callback: function(slideIndex) { + $scope.currentSlide = slideIndex; $scope.$parent.$broadcast('slideBox.slideChanged', slideIndex); $scope.$apply(); } }); - $scope.$parent.slideBox = this; + slider.load(); + + $scope.slider = slider; + + $timeout(function() { + $scope.slider.setup(); + }); }], - template: '
\ -
\ + template: '
\ +
\
\
', @@ -1844,8 +1850,9 @@ angular.module('ionic.ui.slideBox', []) // If the pager should show, append it to the slide box if($attr.showPager !== "false") { var childScope = $scope.$new(); - var pager = $compile('')(childScope); + var pager = angular.element(''); $element.append(pager); + $compile(pager)(childScope); } } }; @@ -1857,10 +1864,9 @@ angular.module('ionic.ui.slideBox', []) replace: true, require: '^slideBox', transclude: true, - template: '
', + template: '
', compile: function(element, attr, transclude) { return function($scope, $element, $attr, slideBoxCtrl) { - slideBoxCtrl.slideAdded(); }; } }; @@ -1871,7 +1877,29 @@ angular.module('ionic.ui.slideBox', []) restrict: 'E', replace: true, require: '^slideBox', - template: '
' + template: '
', + link: function($scope, $element, $attr, slideBox) { + var selectPage = function(index) { + var children = $element[0].children; + var length = children.length; + for(var i = 0; i < length; i++) { + if(i == index) { + children[i].classList.add('active'); + } else { + children[i].classList.remove('active'); + } + } + }; + + $scope.numSlides = function() { + return new Array($scope.slider.getNumSlides()); + }; + + $scope.$watch('currentSlide', function(v) { + console.log('Current slide', v); + selectPage(v); + }); + } }; }); diff --git a/dist/js/ionic.js b/dist/js/ionic.js index 8e0517a684..ef66e9b2bd 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.14 + * Ionic, v0.9.17 * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * @@ -16,7 +16,7 @@ window.ionic = { controllers: {}, views: {}, - version: '0.9.14' + version: '0.9.17' };; (function(ionic) { @@ -4931,342 +4931,587 @@ ionic.views.Scroll = ionic.views.View.inherit({ })(ionic); ; -/** - * The SlideBox is a swipeable, slidable, slideshowable box. Think of any image gallery - * or iOS "dot" pager gallery, or maybe a carousel. +/* + * Adapted from Swipe.js 2.0 * - * Each screen fills the full width and height of the viewport, and screens can - * be swiped between. - */ + * Brad Birdsall + * Copyright 2013, MIT License + * +*/ + (function(ionic) { 'use strict'; - ionic.views.SlideBox = ionic.views.View.inherit({ - initialize: function(opts) { - var _this = this; +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 - this.slideChanged = opts.slideChanged || function() {}; - this.el = opts.el; - this.pager = this.el.querySelector('.slide-box-pager'); + // 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')) + }; - // The drag threshold is the pixel delta that will trigger a drag (to - // avoid accidental dragging) - this.dragThresholdX = opts.dragThresholdX || 10; - // The velocity threshold is a velocity of drag that indicates a "swipe". This - // number is taken from hammer.js's calculations - this.velocityXThreshold = opts.velocityXThreshold || 0.3; - // Initialize the slide index to the first page and update the pager - this.slideIndex = 0; - this._updatePager(); + var container = options.el; - // Listen for drag and release events - window.ionic.onGesture('drag', function(e) { - _this._handleDrag(e); - e.gesture.srcEvent.preventDefault(); - }, this.el); - window.ionic.onGesture('release', function(e) { - _this._handleEndDrag(e); - }, this.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; - /** - * Tell the pager to update itself if content is added or - * removed. - */ - update: function() { - this._updatePager(); - }, + function setup() { - prependSlide: function(el) { - var content = this.el.firstElementChild; - if(!content) { return; } + // cache slides + slides = element.children; + length = slides.length; - var slideWidth = content.offsetWidth; - var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; - var newOffsetX = Math.min(0, offsetX - slideWidth); - - content.insertBefore(el, content.firstChild); + // set continuous to false if only one slide + if (slides.length < 2) options.continuous = false; - content.classList.remove('slide-box-animating'); - content.style.webkitTransform = 'translate3d(' + newOffsetX + 'px, 0, 0)'; - - this._prependPagerIcon(); - this.slideIndex = (this.slideIndex + 1) % content.children.length; - this._updatePager(); - }, - - appendSlide: function(el) { - var content = this.el.firstElementChild; - if(!content) { return; } - - content.classList.remove('slide-box-animating'); - content.appendChild(el); - - this._appendPagerIcon(); - this._updatePager(); - }, - - removeSlide: function(index) { - var content = this.el.firstElementChild; - if(!content) { return; } - - var items = this.el.firstElementChild; - items.removeChild(items.firstElementChild); - - var slideWidth = content.offsetWidth; - var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; - var newOffsetX = Math.min(0, offsetX + slideWidth); - - content.classList.remove('slide-box-animating'); - content.style.webkitTransform = 'translate3d(' + newOffsetX + 'px, 0, 0)'; - - this._removePagerIcon(); - this.slideIndex = Math.max(0, (this.slideIndex - 1) % content.children.length); - this._updatePager(); - }, - - /** - * Slide to the given slide index. - * - * @param {int} the index of the slide to animate to. - */ - slideToSlide: function(index) { - var content = this.el.firstElementChild; - if(!content) { - return; + //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; } - // Get the width of one slide - var slideWidth = content.offsetWidth; + // create an array to store current positions of each slide + slidePos = new Array(slides.length); - // Calculate the new offsetX position which is just - // N slides to the left, where N is the given index - var offsetX = index * slideWidth; + // determine width of each slide + width = container.getBoundingClientRect().width || container.offsetWidth; - // Calculate the max X position we'd allow based on how many slides - // there are. - var maxX = Math.max(0, content.children.length - 1) * slideWidth; + element.style.width = (slides.length * width) + 'px'; - // Bounds the offset X position in the range maxX >= offsetX >= 0 - offsetX = offsetX < 0 ? 0 : offsetX > maxX ? maxX : offsetX; + // stack elements + var pos = slides.length; + while(pos--) { - // Animate and slide the slides over - content.classList.add('slide-box-animating'); - content.style.webkitTransform = 'translate3d(' + -offsetX + 'px, 0, 0)'; + var slide = slides[pos]; - var lastSlide = this.slideIndex; + slide.style.width = width + 'px'; + slide.setAttribute('data-index', pos); - // Update the slide index - this.slideIndex = Math.ceil(offsetX / slideWidth); + if (browser.transitions) { + slide.style.left = (pos * -width) + 'px'; + move(pos, index > pos ? -width : (index < pos ? width : 0), 0); + } - if(lastSlide !== this.slideIndex) { - this.slideChanged && this.slideChanged(this.slideIndex); } - this._updatePager(); - }, - - /** - * Get the currently set slide index. This method - * is updated before any transitions run, so the - * value could be early. - * - * @return {int} the current slide index - */ - getSlideIndex: function() { - return this.slideIndex; - }, - - _appendPagerIcon: function() { - if(!this.pager || !this.pager.children.length) { return; } - - var newPagerChild = this.pager.children[0].cloneNode(); - this.pager.appendChild(newPagerChild); - }, - - _prependPagerIcon: function() { - if(!this.pager || !this.pager.children.length) { return; } - - var newPagerChild = this.pager.children[0].cloneNode(); - this.pager.insertBefore(newPagerChild, this.pager.firstChild); - }, - - _removePagerIcon: function() { - if(!this.pager || !this.pager.children.length) { return; } - - this.pager.removeChild(this.pager.firstElementChild); - }, - - /** - * If we have a pager, update the active page when the current slide - * changes. - */ - _updatePager: function() { - if(!this.pager) { - return; + // reposition elements before and after index + if (options.continuous && browser.transitions) { + move(circle(index-1), -width, 0); + move(circle(index+1), width, 0); } - var numPagerChildren = this.pager.children.length; - if(!numPagerChildren) { - // No children to update - return; - } + if (!browser.transitions) element.style.left = (index * -width) + 'px'; - // Update the active state of the pager icons - for(var i = 0, j = this.pager.children.length; i < j; i++) { - if(i == this.slideIndex) { - this.pager.children[i].classList.add('active'); - } else { - this.pager.children[i].classList.remove('active'); - } - } - }, + container.style.visibility = 'visible'; - _initDrag: function() { - this._isDragging = false; - this._drag = null; - }, - - _handleEndDrag: function(e) { - var _this = this, - finalOffsetX, content, ratio, slideWidth, totalWidth, offsetX; - - window.rAF(function() { - - // We didn't have a drag, so just init and leave - if(!_this._drag) { - _this._initDrag(); - return; - } - - // We did have a drag, so we need to snap to the correct spot - - // Grab the content layer - content = _this._drag.content; - - // Enable transition duration - content.classList.add('slide-box-animating'); - - // Grab the current offset X position - offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; - - // Calculate how wide a single slide is, and their total width - slideWidth = content.offsetWidth; - totalWidth = content.offsetWidth * content.children.length; - - // Calculate how far in this slide we've dragged - ratio = (offsetX % slideWidth) / slideWidth; - - if(ratio >= 0) { - // Anything greater than zero is too far left, this is an extreme case - // TODO: Do we need this anymore? - finalOffsetX = 0; - } else if(ratio >= -0.5) { - // We are less than half-way through a drag - // Sliiide to the left - finalOffsetX = Math.max(0, Math.floor(Math.abs(offsetX) / slideWidth) * slideWidth); - } else { - // We are more than half-way through a drag - // Sliiide to the right - finalOffsetX = Math.min(totalWidth - slideWidth, Math.ceil(Math.abs(offsetX) / slideWidth) * slideWidth); - } - - - if(e.gesture.velocityX > _this.velocityXThreshold) { - if(e.gesture.direction == 'left') { - _this.slideToSlide(_this.slideIndex + 1); - } else if(e.gesture.direction == 'right') { - _this.slideToSlide(_this.slideIndex - 1); - } - } else { - // Calculate the new slide index (or "page") - _this.slideIndex = Math.ceil(finalOffsetX / slideWidth); - - // Negative offsetX to slide correctly - content.style.webkitTransform = 'translate3d(' + -finalOffsetX + 'px, 0, 0)'; - } - - _this._initDrag(); - }); - }, - - /** - * Initialize a drag by grabbing the content area to drag, and any other - * info we might need for the dragging. - */ - _startDrag: function(e) { - var offsetX, content; - - this._initDrag(); - - // Make sure to grab the element we will slide as our target - content = ionic.DomUtil.getParentOrSelfWithClass(e.target, 'slide-box-slides'); - if(!content) { - return; - } - - // Disable transitions during drag - content.classList.remove('slide-box-animating'); - - // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start) - offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; - - this._drag = { - content: content, - startOffsetX: offsetX, - resist: 1 - }; - }, - - /** - * Process the drag event to move the item to the left or right. - */ - _handleDrag: function(e) { - var _this = this; - - window.rAF(function() { - var content; - - // We really aren't dragging - if(!_this._drag) { - _this._startDrag(e); - } - - // Sanity - if(!_this._drag) { return; } - - // Stop any default events during the drag - e.preventDefault(); - - // Check if we should start dragging. Check if we've dragged past the threshold. - if(!_this._isDragging && (Math.abs(e.gesture.deltaX) > _this.dragThresholdX)) { - _this._isDragging = true; - } - - if(_this._isDragging) { - content = _this._drag.content; - - var newX = _this._drag.startOffsetX + (e.gesture.deltaX / _this._drag.resist); - - var rightMostX = -(content.offsetWidth * Math.max(0, content.children.length - 1)); - - if(newX > 0) { - // We are dragging past the leftmost pane, rubber band - _this._drag.resist = (newX / content.offsetWidth) + 1.4; - } else if(newX < rightMostX) { - // Dragging past the rightmost pane, rubber band - //newX = Math.min(rightMostX, + (((e.gesture.deltaX + buttonsWidth) * 0.4))); - _this._drag.resist = (Math.abs(newX) / content.offsetWidth) - 0.6; - } - - _this._drag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; - } - }); + options.slidesChanged && options.slidesChanged(); } - }); -})(window.ionic); + 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); + } + }, + 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) + } + + }, + 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) { 'use strict'; diff --git a/js/ext/angular/src/directive/ionicSlideBox.js b/js/ext/angular/src/directive/ionicSlideBox.js index 616879c39b..63730cdbbd 100644 --- a/js/ext/angular/src/directive/ionicSlideBox.js +++ b/js/ext/angular/src/directive/ionicSlideBox.js @@ -15,32 +15,38 @@ angular.module('ionic.ui.slideBox', []) * some side menu stuff on the current scope. */ -.directive('slideBox', ['$compile', function($compile) { +.directive('slideBox', ['$timeout', '$compile', function($timeout, $compile) { return { restrict: 'E', replace: true, transclude: true, scope: {}, controller: ['$scope', '$element', function($scope, $element) { - $scope.slides = []; - this.slideAdded = function() { - $scope.slides.push({}); - }; + var _this = this; - angular.extend(this, ionic.views.SlideBox.prototype); - - ionic.views.SlideBox.call(this, { + var slider = new ionic.views.Slider({ el: $element[0], - slideChanged: function(slideIndex) { + slidesChanged: function() { + $scope.currentSlide = slider.getPos(); + $timeout(function() {}); + }, + callback: function(slideIndex) { + $scope.currentSlide = slideIndex; $scope.$parent.$broadcast('slideBox.slideChanged', slideIndex); $scope.$apply(); } }); - $scope.$parent.slideBox = this; + slider.load(); + + $scope.slider = slider; + + $timeout(function() { + $scope.slider.setup(); + }); }], - template: '
\ -
\ + template: '
\ +
\
\
', @@ -48,8 +54,9 @@ angular.module('ionic.ui.slideBox', []) // If the pager should show, append it to the slide box if($attr.showPager !== "false") { var childScope = $scope.$new(); - var pager = $compile('')(childScope); + var pager = angular.element(''); $element.append(pager); + $compile(pager)(childScope); } } }; @@ -61,10 +68,9 @@ angular.module('ionic.ui.slideBox', []) replace: true, require: '^slideBox', transclude: true, - template: '
', + template: '
', compile: function(element, attr, transclude) { return function($scope, $element, $attr, slideBoxCtrl) { - slideBoxCtrl.slideAdded(); }; } }; @@ -75,7 +81,29 @@ angular.module('ionic.ui.slideBox', []) restrict: 'E', replace: true, require: '^slideBox', - template: '
' + template: '
', + link: function($scope, $element, $attr, slideBox) { + var selectPage = function(index) { + var children = $element[0].children; + var length = children.length; + for(var i = 0; i < length; i++) { + if(i == index) { + children[i].classList.add('active'); + } else { + children[i].classList.remove('active'); + } + } + }; + + $scope.numSlides = function() { + return new Array($scope.slider.getNumSlides()); + }; + + $scope.$watch('currentSlide', function(v) { + console.log('Current slide', v); + selectPage(v); + }); + } }; }); diff --git a/js/ext/angular/test/infiniteScrollH.html b/js/ext/angular/test/infiniteScrollH.html deleted file mode 100644 index f8d0e9025f..0000000000 --- a/js/ext/angular/test/infiniteScrollH.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - Sldie Box - - - - - - - - - - - - - - - -
- -
-
-

BLUE {{slideBox.slideIndex}}

-
-
-
-
-

YELLOW {{slideBox.slideIndex}}

-
-
-
-

PINK {{slideBox.slideIndex}}

-
-
-
- - - - diff --git a/js/ext/angular/test/slideBox.html b/js/ext/angular/test/slideBox.html index f8d0e9025f..703fe0cca1 100644 --- a/js/ext/angular/test/slideBox.html +++ b/js/ext/angular/test/slideBox.html @@ -1,7 +1,7 @@ - Sldie Box + Slide Box @@ -45,21 +45,21 @@
- -
+ +

BLUE {{slideBox.slideIndex}}

-
-
+ +

YELLOW {{slideBox.slideIndex}}

-
-
+ +

PINK {{slideBox.slideIndex}}

-
-
+ +