UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

560 lines (464 loc) 18.3 kB
/*! * SwipeView v1.0 ~ Copyright (c) 2012 Matteo Spinelli, http://cubiq.org * Released under MIT license, http://cubiq.org/license */ var SwipeView = (function (window, document) { var dummyStyle = document.createElement('div').style, vendor = (function () { var vendors = 't,webkitT,MozT,msT,OT'.split(','), t, i = 0, l = vendors.length; for ( ; i < l; i++ ) { t = vendors[i] + 'ransform'; if ( t in dummyStyle ) { return vendors[i].substr(0, vendors[i].length - 1); } } return false; })(), cssVendor = vendor ? '-' + vendor.toLowerCase() + '-' : '', // Style properties transform = prefixStyle('transform'), transitionDuration = prefixStyle('transitionDuration'), // Browser capabilities has3d = prefixStyle('perspective') in dummyStyle, hasTouch = 'ontouchstart' in window, hasTransform = !!vendor, hasTransitionEnd = prefixStyle('transition') in dummyStyle, // Helpers translateZ = has3d ? ' translateZ(0)' : '', // Events resizeEvent = 'onorientationchange' in window ? 'orientationchange' : 'resize', startEvent = hasTouch ? 'touchstart' : 'mousedown', moveEvent = hasTouch ? 'touchmove' : 'mousemove', endEvent = hasTouch ? 'touchend' : 'mouseup', cancelEvent = hasTouch ? 'touchcancel' : 'mouseup', transitionEndEvent = (function () { if ( vendor === false ) return false; var transitionEnd = { '' : 'transitionend', 'webkit' : 'webkitTransitionEnd', 'Moz' : 'transitionend', 'O' : 'oTransitionEnd', 'ms' : 'MSTransitionEnd' }; return transitionEnd[vendor]; })(), SwipeView = function (el, options) { var i, div, className, pageIndex; this.wrapper = typeof el == 'string' ? document.querySelector(el) : el; this.options = { text: null, numberOfPages: 3, snapThreshold: null, hastyPageFlip: false, loop: true }; // User defined options for (i in options) this.options[i] = options[i]; this.wrapper.style.overflow = 'hidden'; this.wrapper.style.position = 'relative'; this.masterPages = []; div = document.createElement('div'); //MODIFIED BY SAP //make slider id unique div.id = this.wrapper.parentElement.id + '-slider'; //MODIFIED BY SAP //changed position to 'absolute', otherwise problems with carousels //with a fixed height, for example '156px' or '28em' div.style.cssText = 'position:absolute;top:0;height:100%;width:100%;' + cssVendor + 'transition-duration:0;' + cssVendor + 'transform:translateZ(0);' + cssVendor + 'transition-timing-function:ease-out'; this.wrapper.appendChild(div); this.slider = div; this.refreshSize(); for (i=-1; i<2; i++) { div = document.createElement('div'); //MODIFIED BY SAP //make master page id unique div.id = this.wrapper.parentElement.id + '-masterpage-' + (i+1); div.style.cssText = cssVendor + 'transform:translateZ(0);position:absolute;top:0;height:100%;width:100%;left:' + i*100 + '%'; if (!div.dataset) div.dataset = {}; pageIndex = i == -1 ? this.options.numberOfPages - 1 : i; div.dataset.pageIndex = pageIndex; div.dataset.upcomingPageIndex = pageIndex; if (!this.options.loop && i == -1) div.style.visibility = 'hidden'; this.slider.appendChild(div); this.masterPages.push(div); } className = this.masterPages[1].className; this.masterPages[1].className = !className ? 'swipeview-active' : className + ' swipeview-active'; window.addEventListener(resizeEvent, this, false); this.wrapper.addEventListener(startEvent, this, false); this.wrapper.addEventListener(moveEvent, this, false); this.wrapper.addEventListener(endEvent, this, false); this.slider.addEventListener(transitionEndEvent, this, false); // in Opera >= 12 the transitionend event is lowercase so we register both events if ( vendor == 'O' ) this.slider.addEventListener(transitionEndEvent.toLowerCase(), this, false); //MODIFIED BY SAP //Please refer to comment for function 'initialSizeCheck' this._bPerformInitialSizeCheck = true; /* if (!hasTouch) { this.wrapper.addEventListener('mouseout', this, false); }*/ }; SwipeView.prototype = { currentMasterPage: 1, x: 0, page: 0, pageIndex: 0, customEvents: [], onFlip: function (fn) { this.wrapper.addEventListener('swipeview-flip', fn, false); this.customEvents.push(['flip', fn]); }, onMoveOut: function (fn) { this.wrapper.addEventListener('swipeview-moveout', fn, false); this.customEvents.push(['moveout', fn]); }, onMoveIn: function (fn) { this.wrapper.addEventListener('swipeview-movein', fn, false); this.customEvents.push(['movein', fn]); }, onTouchStart: function (fn) { this.wrapper.addEventListener('swipeview-touchstart', fn, false); this.customEvents.push(['touchstart', fn]); }, destroy: function () { while ( this.customEvents.length ) { this.wrapper.removeEventListener('swipeview-' + this.customEvents[0][0], this.customEvents[0][1], false); this.customEvents.shift(); } // Remove the event listeners window.removeEventListener(resizeEvent, this, false); this.wrapper.removeEventListener(startEvent, this, false); this.wrapper.removeEventListener(moveEvent, this, false); this.wrapper.removeEventListener(endEvent, this, false); this.slider.removeEventListener(transitionEndEvent, this, false); /* if (!hasTouch) { this.wrapper.removeEventListener('mouseout', this, false); }*/ //MODIFIED BY SAP //remove callback reference this.fnLoadingCallback = null; }, refreshSize: function () { this.wrapperWidth = this.wrapper.clientWidth; this.wrapperHeight = this.wrapper.clientHeight; this.pageWidth = this.wrapperWidth; this.maxX = -this.options.numberOfPages * this.pageWidth + this.wrapperWidth; this.snapThreshold = this.options.snapThreshold === null ? Math.round(this.pageWidth * 0.15) : /%/.test(this.options.snapThreshold) ? Math.round(this.pageWidth * this.options.snapThreshold.replace('%', '') / 100) : this.options.snapThreshold; }, updatePageCount: function (n) { this.options.numberOfPages = n; this.maxX = -this.options.numberOfPages * this.pageWidth + this.wrapperWidth; }, goToPage: function (p) { var i; this.masterPages[this.currentMasterPage].className = this.masterPages[this.currentMasterPage].className.replace(/(^|\s)swipeview-active(\s|$)/, ''); for (i=0; i<3; i++) { className = this.masterPages[i].className; /(^|\s)swipeview-loading(\s|$)/.test(className) || (this.masterPages[i].className = !className ? 'swipeview-loading' : className + ' swipeview-loading'); //MODIFIED BY SAP //apply class change to carousel callback if(this.fnLoadingCallback) { this.fnLoadingCallback.call(undefined, i, true); } } p = p < 0 ? 0 : p > this.options.numberOfPages-1 ? this.options.numberOfPages-1 : p; this.page = p; this.pageIndex = p; this.slider.style[transitionDuration] = '0s'; this.__pos(-p * this.pageWidth); this.currentMasterPage = (this.page + 1) - Math.floor((this.page + 1) / 3) * 3; this.masterPages[this.currentMasterPage].className = this.masterPages[this.currentMasterPage].className + ' swipeview-active'; if (this.currentMasterPage === 0) { this.masterPages[2].style.left = this.page * 100 - 100 + '%'; this.masterPages[0].style.left = this.page * 100 + '%'; this.masterPages[1].style.left = this.page * 100 + 100 + '%'; this.masterPages[2].dataset.upcomingPageIndex = this.page === 0 ? this.options.numberOfPages-1 : this.page - 1; this.masterPages[0].dataset.upcomingPageIndex = this.page; this.masterPages[1].dataset.upcomingPageIndex = this.page == this.options.numberOfPages-1 ? 0 : this.page + 1; } else if (this.currentMasterPage == 1) { this.masterPages[0].style.left = this.page * 100 - 100 + '%'; this.masterPages[1].style.left = this.page * 100 + '%'; this.masterPages[2].style.left = this.page * 100 + 100 + '%'; this.masterPages[0].dataset.upcomingPageIndex = this.page === 0 ? this.options.numberOfPages-1 : this.page - 1; this.masterPages[1].dataset.upcomingPageIndex = this.page; this.masterPages[2].dataset.upcomingPageIndex = this.page == this.options.numberOfPages-1 ? 0 : this.page + 1; } else { this.masterPages[1].style.left = this.page * 100 - 100 + '%'; this.masterPages[2].style.left = this.page * 100 + '%'; this.masterPages[0].style.left = this.page * 100 + 100 + '%'; this.masterPages[1].dataset.upcomingPageIndex = this.page === 0 ? this.options.numberOfPages-1 : this.page - 1; this.masterPages[2].dataset.upcomingPageIndex = this.page; this.masterPages[0].dataset.upcomingPageIndex = this.page == this.options.numberOfPages-1 ? 0 : this.page + 1; } this.__flip(); }, next: function () { //MODIFIED BY SAP: using pageIndex instead of this.x because it leads to errors if (!this.options.loop && this.pageIndex === this.options.numberOfPages - 1) return; //MODIFIED BY SAP //Please refer to comment for function 'initialSizeCheck' this.initialSizeCheck(); this.directionX = -1; this.x -= 1; this.__checkPosition(); }, prev: function () { //MODIFIED BY SAP: using pageIndex instead of this.x because it leads to errors if (!this.options.loop && this.pageIndex === 0) return; //MODIFIED BY SAP //Please refer to comment for function 'initialSizeCheck' this.initialSizeCheck(); this.directionX = 1; this.x += 1; this.__checkPosition(); }, handleEvent: function (e) { switch (e.type) { case startEvent: this.__start(e); break; case moveEvent: this.__move(e); break; case cancelEvent: case endEvent: this.__end(e); break; case resizeEvent: //MODIFIED BY SAP //Use sap.ui.core.ResizeHandler instead //this.__resize(); break; case transitionEndEvent: case 'otransitionend': if (e.target == this.slider && !this.options.hastyPageFlip) this.__flip(); break; } }, //MODIFIED BY SAP //If the default width is set the swipeview has a bug when calculating the swipe //distance. if that distance is wrong, controls are not centered after a //swipe. To fix that bug, swipe view's 'refreshSize' function is called once initially //after first rendering. Therefore, the additional function 'initialSizeCheck' has been introduced initialSizeCheck: function() { if(this._bPerformInitialSizeCheck) { this.refreshSize(); this._bPerformInitialSizeCheck = false; } }, /** * * Pseudo private methods * */ __pos: function (x) { this.x = x; this.slider.style[transform] = 'translate(' + x + 'px,0)' + translateZ; }, __resize: function () { this.refreshSize(); this.slider.style[transitionDuration] = '0s'; this.__pos(-this.page * this.pageWidth); }, __start: function (e) { //e.preventDefault(); // BEGIN: MODIFIED BY SAP //if move event has already been consumed by let's say switch ore slider control, //we should not act on it any more if(!!e.originalEvent && !!e.originalEvent._sapui_handledByControl) { return; } // END: MODIFIED BY SAP if (this.initiated) return; var point = hasTouch ? e.touches[0] : e; this.initiated = true; this.moved = false; this.thresholdExceeded = false; this.startX = point.pageX; this.startY = point.pageY; this.pointX = point.pageX; this.pointY = point.pageY; this.stepsX = 0; this.stepsY = 0; this.directionX = 0; this.directionLocked = false; /* var matrix = getComputedStyle(this.slider, null).webkitTransform.replace(/[^0-9-.,]/g, '').split(','); this.x = matrix[4] * 1;*/ this.slider.style[transitionDuration] = '0s'; this.__event('touchstart'); }, __move: function (e) { if (!this.initiated) return; var point = hasTouch ? e.touches[0] : e, deltaX = point.pageX - this.pointX, deltaY = point.pageY - this.pointY, newX = this.x + deltaX, dist = Math.abs(point.pageX - this.startX); this.moved = true; this.pointX = point.pageX; this.pointY = point.pageY; this.directionX = deltaX > 0 ? 1 : deltaX < 0 ? -1 : 0; this.stepsX += Math.abs(deltaX); this.stepsY += Math.abs(deltaY); // We take a 10px buffer to figure out the direction of the swipe if (this.stepsX < 10 && this.stepsY < 10) { // e.preventDefault(); return; } // We are scrolling vertically, so skip SwipeView and give the control back to the browser if (!this.directionLocked && this.stepsY > this.stepsX) { this.initiated = false; return; } e.preventDefault(); this.directionLocked = true; if (!this.options.loop && (newX > 0 || newX < this.maxX)) { // BEGIN: MODIFIED BY SAP //Make sure rubber band effect is only shown for IOS if(sap.ui.Device.os.ios) { newX = this.x + (deltaX / 2); } else { //No rubber-band for non ios! if(newX > 0 ) { newX = 0; } else { newX = this.maxX; } } // END: MODIFIED BY SAP } if (!this.thresholdExceeded && dist >= this.snapThreshold) { this.thresholdExceeded = true; this.__event('moveout'); } else if (this.thresholdExceeded && dist < this.snapThreshold) { this.thresholdExceeded = false; this.__event('movein'); } /* if (newX > 0 || newX < this.maxX) { newX = this.x + (deltaX / 2); }*/ this.__pos(newX); }, __end: function (e) { if (!this.initiated) return; var point = hasTouch ? e.changedTouches[0] : e, dist = Math.abs(point.pageX - this.startX); this.initiated = false; if (!this.moved) return; // BEGIN: MODIFIED BY SAP //Make sure rubber band effect is shown for left- //and rightmost pages, too var bMoveBack = false; if (!this.options.loop && (this.x > 0 || this.x < this.maxX)) { this.__event('movein'); bMoveBack = true; } // Check if we exceeded the snap threshold if (bMoveBack || dist < this.snapThreshold) { this.slider.style[transitionDuration] = Math.floor(100 * dist / this.snapThreshold) + 'ms'; this.__pos(-this.page * this.pageWidth); return; } // END: MODIFIED BY SAP this.__checkPosition(); }, __checkPosition: function () { var pageFlip, pageFlipIndex, className; // BEGIN: MODIFIED BY SAP //Make sure that swipe works correctly in loop mode with less than 3 pages if(this.options.loop) { if(this.options.numberOfPages === 1) { this.goToPage(0); } else if(this.options.numberOfPages === 2) { if (this.directionX < 0) { if(this.page === 1) { this.goToPage(0); } } else { if(this.page === 0) { this.goToPage(1); } } } } // END: MODIFIED BY SAP this.masterPages[this.currentMasterPage].className = this.masterPages[this.currentMasterPage].className.replace(/(^|\s)swipeview-active(\s|$)/, ''); // Flip the page if (this.directionX > 0) { this.page = -Math.ceil(this.x / this.pageWidth); this.currentMasterPage = (this.page + 1) - Math.floor((this.page + 1) / 3) * 3; this.pageIndex = this.pageIndex === 0 ? this.options.numberOfPages - 1 : this.pageIndex - 1; pageFlip = this.currentMasterPage - 1; pageFlip = pageFlip < 0 ? 2 : pageFlip; this.masterPages[pageFlip].style.left = this.page * 100 - 100 + '%'; pageFlipIndex = this.page - 1; } else { this.page = -Math.floor(this.x / this.pageWidth); this.currentMasterPage = (this.page + 1) - Math.floor((this.page + 1) / 3) * 3; this.pageIndex = this.pageIndex == this.options.numberOfPages - 1 ? 0 : this.pageIndex + 1; pageFlip = this.currentMasterPage + 1; pageFlip = pageFlip > 2 ? 0 : pageFlip; this.masterPages[pageFlip].style.left = this.page * 100 + 100 + '%'; pageFlipIndex = this.page + 1; } // Add active class to current page className = this.masterPages[this.currentMasterPage].className; /(^|\s)swipeview-active(\s|$)/.test(className) || (this.masterPages[this.currentMasterPage].className = !className ? 'swipeview-active' : className + ' swipeview-active'); // Add loading class to flipped page className = this.masterPages[pageFlip].className; /(^|\s)swipeview-loading(\s|$)/.test(className) || (this.masterPages[pageFlip].className = !className ? 'swipeview-loading' : className + ' swipeview-loading'); //MODIFIED BY SAP //apply class change to carousel callback if(this.fnLoadingCallback) { this.fnLoadingCallback.call(undefined, pageFlip, true); } pageFlipIndex = pageFlipIndex - Math.floor(pageFlipIndex / this.options.numberOfPages) * this.options.numberOfPages; this.masterPages[pageFlip].dataset.upcomingPageIndex = pageFlipIndex; // Index to be loaded in the newly flipped page newX = -this.page * this.pageWidth; this.slider.style[transitionDuration] = Math.floor(500 * Math.abs(this.x - newX) / this.pageWidth) + 'ms'; // Hide the next page if we decided to disable looping if (!this.options.loop) { this.masterPages[pageFlip].style.visibility = newX === 0 || newX == this.maxX ? 'hidden' : ''; } if (this.x == newX) { this.__flip(); // If we swiped all the way long to the next page (extremely rare but still) } else { this.__pos(newX); if (this.options.hastyPageFlip) this.__flip(); } }, __flip: function () { //MODIFIED BY SAP //Please refer to comment for function 'initialSizeCheck' this.initialSizeCheck(); this.__event('flip'); for (var i=0; i<3; i++) { this.masterPages[i].className = this.masterPages[i].className.replace(/(^|\s)swipeview-loading(\s|$)/, ''); // Remove the loading class this.masterPages[i].dataset.pageIndex = this.masterPages[i].dataset.upcomingPageIndex; } }, __event: function (type) { var ev = document.createEvent("Event"); ev.initEvent('swipeview-' + type, true, true); this.wrapper.dispatchEvent(ev); } }; function prefixStyle (style) { if ( vendor === '' ) return style; style = style.charAt(0).toUpperCase() + style.substr(1); return vendor + style; } return SwipeView; })(window, document);