diff --git a/js/utils/activator.js b/js/utils/activator.js index 8be60e38e9..62ac608106 100644 --- a/js/utils/activator.js +++ b/js/utils/activator.js @@ -68,7 +68,6 @@ } function activateElements() { - // console.log('ACTIVATING'); // activate all elements in the queue for(var key in queueElements) { if(queueElements[key]) { diff --git a/js/utils/keyboard.js b/js/utils/keyboard.js index a7f367ccbb..c1a4e74bf4 100644 --- a/js/utils/keyboard.js +++ b/js/utils/keyboard.js @@ -16,18 +16,22 @@ var SCROLL_CONTAINER_CSS = 'scroll'; ionic.keyboard = { isOpen: false, - height: null + height: null, + landscape: false, }; function keyboardInit() { if( keyboardHasPlugin() ) { window.addEventListener('native.showkeyboard', keyboardNativeShow); + window.addEventListener('native.hidekeyboard', keyboardFocusOut); + } + else { + document.body.addEventListener('focusout', keyboardFocusOut); } document.body.addEventListener('ionic.focusin', keyboardBrowserFocusIn); document.body.addEventListener('focusin', keyboardBrowserFocusIn); - document.body.addEventListener('focusout', keyboardFocusOut); document.body.addEventListener('orientationchange', keyboardOrientationChange); document.removeEventListener('touchstart', keyboardInit); @@ -55,10 +59,25 @@ function keyboardSetShow(e) { clearTimeout(keyboardFocusOutTimer); keyboardFocusInTimer = setTimeout(function(){ - var keyboardHeight = keyboardGetHeight(); + var keyboardHeight; var elementBounds = keyboardActiveElement.getBoundingClientRect(); + var count = 0; - keyboardShow(e.target, elementBounds.top, elementBounds.bottom, keyboardViewportHeight, keyboardHeight); + var pollKeyboardHeight = setInterval(function(){ + + keyboardHeight = keyboardGetHeight(); + if (count > 10){ + clearInterval(pollKeyboardHeight); + //waited long enough, just guess + keyboardHeight = 275; + } + if (keyboardHeight){ + keyboardShow(e.target, elementBounds.top, elementBounds.bottom, keyboardViewportHeight, keyboardHeight); + clearInterval(pollKeyboardHeight); + } + count++; + + }, 100); }, 32); } @@ -70,15 +89,9 @@ function keyboardShow(element, elementTop, elementBottom, viewportHeight, keyboa keyboardHeight: keyboardHeight }; - if( keyboardIsOverWebView() ) { - // keyboard sits on top of the view, but doesn't adjust the view's height - // lower the content height by subtracting the keyboard height from the view height - details.contentHeight = viewportHeight - keyboardHeight; - } else { - // view's height was shrunk down and the keyboard takes up the space the view doesn't fill - // do not add extra padding at the bottom of the scroll view, native already did that - details.contentHeight = viewportHeight; - } + details.hasPlugin = keyboardHasPlugin(); + + details.contentHeight = viewportHeight - keyboardHeight; console.debug('keyboardShow', keyboardHeight, details.contentHeight); @@ -149,32 +162,70 @@ function keyboardPreventDefault(e) { } function keyboardOrientationChange() { - keyboardViewportHeight = window.innerHeight; - setTimeout(function(){ - keyboardViewportHeight = window.innerHeight; - }, 999); + var updatedViewportHeight = window.innerHeight; + + //too slow, have to wait for updated height + if (updatedViewportHeight === keyboardViewportHeight){ + var count = 0; + var pollViewportHeight = setInterval(function(){ + //give up + if (count > 10){ + clearInterval(pollViewportHeight); + } + + updatedViewportHeight = window.innerHeight; + + if (updatedViewportHeight !== keyboardViewportHeight){ + if (updatedViewportHeight < keyboardViewportHeight){ + ionic.keyboard.landscape = true; + } + else { + ionic.keyboard.landscape = false; + } + keyboardViewportHeight = updatedViewportHeight; + clearInterval(pollViewportHeight); + } + count++; + + }, 50); + } + else { + keyboardViewportHeight = updatedViewportHeight; + } } function keyboardGetHeight() { // check if we are already have a keyboard height from the plugin - if (ionic.keyboard.height ) { + if ( ionic.keyboard.height ) { return ionic.keyboard.height; } + if ( ionic.Platform.isAndroid() ){ + //should be using the plugin, no way to know how big the keyboard is, so guess + if ( ionic.Platform.isFullScreen ){ + return 275; + } + //otherwise, wait for the screen to resize + if ( window.innerHeight < keyboardViewportHeight ){ + return keyboardViewportHeight - window.innerHeight; + } + else { + return 0; + } + } + // fallback for when its the webview without the plugin // or for just the standard web browser if( ionic.Platform.isIOS() ) { - if( ionic.Platform.isWebView() ) { - return 260; + if ( ionic.keyboard.landscape ){ + return 206; } - return 216; - } else if( ionic.Platform.isAndroid() ) { - if( ionic.Platform.isWebView() ) { - return 220; - } - if( ionic.Platform.version() <= 4.3) { - return 230; + + if (!ionic.Platform.isWebView()){ + return 216; } + + return 260; } // safe guess @@ -191,11 +242,6 @@ function keyboardIsWithinScroll(ele) { return false; } -function keyboardIsOverWebView() { - return ( ionic.Platform.isIOS() ) || - ( ionic.Platform.isAndroid() && !ionic.Platform.isWebView() ); -} - function keyboardHasPlugin() { return !!(window.cordova && cordova.plugins && cordova.plugins.Keyboard); } diff --git a/js/utils/platform.js b/js/utils/platform.js index 129cdbc1a7..5af319faef 100644 --- a/js/utils/platform.js +++ b/js/utils/platform.js @@ -90,6 +90,8 @@ if(this.isWebView()) { this.platforms.push('webview'); this.platforms.push('cordova'); + } else { + this.platforms.push('browser'); } if(this.isIPad()) this.platforms.push('ipad'); @@ -129,7 +131,10 @@ * @returns {boolean} Whether we are running on iPad. */ isIPad: function() { - return this.ua.toLowerCase().indexOf('ipad') >= 0; + if( /iPad/i.test(window.navigator.platform) ) { + return true; + } + return /iPad/i.test(this.ua); }, /** * @ngdoc method @@ -170,7 +175,7 @@ } else if(this.ua.indexOf('iPhone') > -1 || this.ua.indexOf('iPad') > -1 || this.ua.indexOf('iPod') > -1) { platformName = 'ios'; } else { - platformName = ''; + platformName = window.navigator.platform && window.navigator.platform.toLowerCase().split(' ')[0] || ''; } }, diff --git a/js/utils/viewport.js b/js/utils/viewport.js index 68ef3cee67..806b59b5f8 100644 --- a/js/utils/viewport.js +++ b/js/utils/viewport.js @@ -2,6 +2,14 @@ var viewportTag; var viewportProperties = {}; +ionic.viewport = { + orientation: function() { + // 0 = Portrait + // 90 = Landscape + // not using window.orientation because each device has a different implementation + return (window.innerWidth > window.innerHeight ? 90 : 0); + } +}; function viewportLoadTag() { var x; @@ -17,41 +25,110 @@ function viewportLoadTag() { var props = viewportTag.content.toLowerCase().replace(/\s+/g, '').split(','); var keyValue; for(x=0; x 1 ? keyValue[1] : '_'); + } } - viewportInitWebView(); + viewportUpdate(); } } -function viewportInitWebView() { - var hasViewportChange = false; +function viewportUpdate() { + // unit tests in viewport.unit.js - if( ionic.Platform.isWebView() ) { - if( viewportProperties.height != 'device-height' ) { - viewportProperties.height = 'device-height'; - hasViewportChange = true; + var initWidth = viewportProperties.width; + var initHeight = viewportProperties.height; + var p = ionic.Platform; + var version = p.version(); + var DEVICE_WIDTH = 'device-width'; + var DEVICE_HEIGHT = 'device-height'; + var orientation = ionic.viewport.orientation(); + + // Most times we're removing the height and adding the width + // So this is the default to start with, then modify per platform/version/oreintation + delete viewportProperties.height; + viewportProperties.width = DEVICE_WIDTH; + + if( p.isIPad() ) { + // iPad + + if( version > 7 ) { + // iPad >= 7.1 + // https://issues.apache.org/jira/browse/CB-4323 + delete viewportProperties.width; + + } else { + // iPad <= 7.0 + + if( p.isWebView() ) { + // iPad <= 7.0 WebView + + if( orientation == 90 ) { + // iPad <= 7.0 WebView Landscape + viewportProperties.height = '0'; + + } else if(version == 7) { + // iPad <= 7.0 WebView Portait + viewportProperties.height = DEVICE_HEIGHT; + } + } else { + // iPad <= 6.1 Browser + if(version < 7) { + viewportProperties.height = '0'; + } + } } - } else if( viewportProperties.height ) { - delete viewportProperties.height; - hasViewportChange = true; + + } else if( p.isIOS() ) { + // iPhone + + if( p.isWebView() ) { + // iPhone WebView + + if(version > 7) { + // iPhone >= 7.1 WebView + delete viewportProperties.width; + + } else if(version < 7) { + // iPhone <= 6.1 WebView + // if height was set it needs to get removed with this hack for <= 6.1 + if( initHeight ) viewportProperties.height = '0'; + } + + } else { + // iPhone Browser + + if (version < 7) { + // iPhone <= 6.1 Browser + // if height was set it needs to get removed with this hack for <= 6.1 + if( initHeight ) viewportProperties.height = '0'; + } + } + + } + + // only update the viewport tag if there was a change + if(initWidth !== viewportProperties.width || initHeight !== viewportProperties.height) { + viewportTagUpdate(); } - if(hasViewportChange) viewportUpdate(); } -function viewportUpdate(updates) { - if(!viewportTag) return; - - ionic.Utils.extend(viewportProperties, updates); - +function viewportTagUpdate() { var key, props = []; for(key in viewportProperties) { - if(viewportProperties[key]) props.push(key + '=' + viewportProperties[key]); + if( viewportProperties[key] ) { + props.push(key + (viewportProperties[key] == '_' ? '' : '=' + viewportProperties[key]) ); + } } - viewportTag.content = props.join(','); + viewportTag.content = props.join(', '); } ionic.DomUtil.ready(function() { viewportLoadTag(); + + window.addEventListener("orientationchange", function(){ + setTimeout(viewportUpdate, 1000); + }, false); }); diff --git a/js/views/scrollView.js b/js/views/scrollView.js index 26c3f32255..966f77edc1 100644 --- a/js/views/scrollView.js +++ b/js/views/scrollView.js @@ -622,15 +622,26 @@ ionic.views.Scroll = ionic.views.View.inherit({ if( !self.isScrolledIntoView ) { // shrink scrollview so we can actually scroll if the input is hidden // if it isn't shrink so we can scroll to inputs under the keyboard - container.style.height = (container.clientHeight - e.detail.keyboardHeight) + "px"; - container.style.overflow = "visible"; + if (ionic.Platform.isIOS() || ionic.Platform.isFullScreen){ + container.style.height = (container.clientHeight - e.detail.keyboardHeight) + "px"; + container.style.overflow = "visible"; + //update scroll view + self.resize(); + } self.isScrolledIntoView = true; - //update scroll view - self.resize(); } //If the element is positioned under the keyboard... if( e.detail.isElementUnderKeyboard ) { + var delay; + // Wait on android for scroll view to resize + if ( !ionic.Platform.isFullScreen && e.detail.hasPlugin ) { + delay = 350; + } + else { + delay = 80; + } + //Put element in middle of visible screen //Wait for resize() to reset scroll position ionic.scroll.isScrolling = true; @@ -642,9 +653,7 @@ ionic.views.Scroll = ionic.views.View.inherit({ ionic.tap.cloneFocusedInput(container, self); self.scrollBy(0, scrollTop, true); self.onScroll(); - }, - (ionic.Platform.isIOS() ? 80 : 350) - ); + }, delay); } //Only the first scrollView parent of the element that broadcasted this event diff --git a/scss/_platform.scss b/scss/_platform.scss index bee3b8a8c1..edd3c46ba2 100644 --- a/scss/_platform.scss +++ b/scss/_platform.scss @@ -46,8 +46,15 @@ } } +@media (orientation:landscape) { + .platform-ios7.platform-browser.platform-ipad { + position: fixed; // required for iPad 7 Safari + } +} + .platform-android2:not(.enable-transitions) * { // disable transitions on Android 2 -webkit-transition: none !important; transition: none !important; } + diff --git a/test/html/viewState.html b/test/html/viewState.html index 16ab9d04d9..cda40510d4 100644 --- a/test/html/viewState.html +++ b/test/html/viewState.html @@ -261,6 +261,42 @@ + + + + + + + + + + + +
diff --git a/test/unit/angular/service/platform.unit.js b/test/unit/angular/service/platform.unit.js index 407cc69f2a..762bcb9bd0 100644 --- a/test/unit/angular/service/platform.unit.js +++ b/test/unit/angular/service/platform.unit.js @@ -5,6 +5,9 @@ describe('Ionic Platform Service', function() { beforeEach(inject(function($window, $ionicPlatform, $rootScope) { window = $window; + window.navigator = { + platform: '' + }; ionic.Platform.ua = ''; ionicPlatform = $ionicPlatform; rootScope = $rootScope; @@ -84,6 +87,39 @@ describe('Ionic Platform Service', function() { expect(ionic.Platform.version()).toEqual(7.0); }); + it('should not be iPad from none iPad user agent', function() { + ionic.Platform.ua = 'Mozilla/5.0 (iPhone; CPU OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53'; + ionic.Platform.setPlatform(undefined); + ionic.Platform.setVersion(undefined); + expect(ionic.Platform.isIPad()).toEqual(false); + }); + + it('should be iPad from user agent', function() { + ionic.Platform.ua = 'Mozilla/5.0 (iPad; CPU OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53'; + ionic.Platform.setPlatform(undefined); + ionic.Platform.setVersion(undefined); + expect(ionic.Platform.isIPad()).toEqual(true); + }); + + it('should be iPad from iPad in window.navigator.platform and webview, but iPhone in user agent', function() { + window.cordova = {}; + ionic.Platform.ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'; + window.navigator = { + platform: 'iPad Simulator' + }; + ionic.Platform.setPlatform(undefined); + ionic.Platform.setVersion(undefined); + expect(ionic.Platform.isIPad()).toEqual(true); + }); + + it('should not be iPad from no in window.navigator.platform, and iPhone in user agent', function() { + ionic.Platform.ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'; + window.navigator = {}; + ionic.Platform.setPlatform(undefined); + ionic.Platform.setVersion(undefined); + expect(ionic.Platform.isIPad()).toEqual(false); + }); + it('is iOS', function() { ionic.Platform.setPlatform('iOS'); expect(ionic.Platform.isIOS()).toEqual(true); @@ -104,6 +140,7 @@ describe('Ionic Platform Service', function() { }); it('is WebView', function() { + window.cordova = undefined; expect(ionic.Platform.isWebView()).toEqual(false); window.cordova = {}; expect(ionic.Platform.isWebView()).toEqual(true); @@ -155,7 +192,7 @@ describe('Ionic Platform Service', function() { expect(ionic.Platform.platforms[1]).toEqual('cordova'); }); - it('should not set any platform', function() { + it('should not set if its not a webview but only a browser', function() { window.cordova = null; window.PhoneGap = null; window.phonegap = null; @@ -164,7 +201,7 @@ describe('Ionic Platform Service', function() { ionic.Platform._checkPlatforms() - expect(ionic.Platform.platforms.length).toEqual(0); + expect(ionic.Platform.platforms[0]).toEqual('browser'); }); it('sets grade a from iOS7', function() { diff --git a/test/unit/utils/keyboard.unit.js b/test/unit/utils/keyboard.unit.js index 9b10221f85..9e56bd46ed 100644 --- a/test/unit/utils/keyboard.unit.js +++ b/test/unit/utils/keyboard.unit.js @@ -41,6 +41,31 @@ Tested On - Android 4.2 Cordova +iOS 7.1 Cordova with AND without viewport height DOES resize, DOES NOT fire resize event +iOS 7.1 Safari with AND without viewport height DOES NOT resize + +iOS 7.0 Cordova with viewport height DOES resize, DOES fire resize event +iOS 7.0 Cordova without viewport height DOES resize, DOES NOT fire resize event +iOS 7.0 Safari with AND without viewport height DOES NOT resize + +iOS 6.1 Cordova with AND without viewport height DOES NOT resize +iOS 6.1 Safari without viewport height DOES NOT resize + +NOTES: + -iOS 7.1 Safari with viewport height screws up ionic layout + -iOS 7.0 Safari with viewport height, the scroll view does not resize properly on keyboardhide + -iOS 7.0 Cordova without viewport height, scroll view does not resize properly switching inputs at bottom of page + -iOS 6.1 Cordova and Safari don't work well with viewport height + +RECOMMENDATIONS: + -iOS 7.1 Cordova no viewport height, keyboard is not over webview + -iOS 7.1 Safari no viewport height, keyboard is over webview + + -iOS 7.0 Cordova yes viewport height, keyboard is not over webview + -iOS 7.0 Safari no viewport height, keyboard is over webview + + -iOS 6.1 Cordova no viewport height, keyboard is over webview + -iOS 6.1 Safari no viewport height, keyboard is over webview Notes: --------------------------------- @@ -65,6 +90,9 @@ describe('Ionic Keyboard', function() { ionic.Platform.setPlatform(''); ionic.Platform.setVersion(''); ionic.keyboard.isOpen = false; + ionic.keyboard.height = null; + ionic.Platform.isFullScreen = false; + ionic.keyboard.landscape = false; })); afterEach(function(){ @@ -82,45 +110,6 @@ describe('Ionic Keyboard', function() { expect( details.keyboardHeight ).toEqual(200); }); - it('Should keyboardIsOverWebView()=false if Android and not isWebView', function(){ - // Android browser places the keyboard on top of the content and doesn't resize the window - ionic.Platform.setPlatform('Android'); - expect( ionic.Platform.isAndroid() ).toEqual(true); - expect( ionic.Platform.isWebView() ).toEqual(false); - - expect( ionic.Platform.isIOS() ).toEqual(false); - - expect( keyboardIsOverWebView() ).toEqual(true); - }); - - it('Should keyboardIsOverWebView()=false if Android and isWebView', function(){ - // Android webview gets shrunk by cordova and the keyboard fills the gap - ionic.Platform.setPlatform('Android'); - window.cordova = {}; - expect( ionic.Platform.isAndroid() ).toEqual(true); - expect( ionic.Platform.isWebView() ).toEqual(true); - - expect( keyboardIsOverWebView() ).toEqual(false); - }); - - it('Should keyboardIsOverWebView()=true if iOS 7.0 or greater', function(){ - ionic.Platform.setPlatform('iOS'); - ionic.Platform.setVersion('7.0'); - expect( ionic.Platform.isAndroid() ).toEqual(false); - expect( ionic.Platform.isIOS() ).toEqual(true); - - expect( keyboardIsOverWebView() ).toEqual(true); - }); - - it('Should keyboardIsOverWebView()=true if less than iOS 7.0', function(){ - ionic.Platform.setPlatform('iOS'); - ionic.Platform.setVersion('6.0'); - expect( ionic.Platform.isAndroid() ).toEqual(false); - expect( ionic.Platform.isIOS() ).toEqual(true); - - expect( keyboardIsOverWebView() ).toEqual(true); - }); - it('Should keyboardHasPlugin', function() { expect( keyboardHasPlugin() ).toEqual(false); @@ -134,10 +123,67 @@ describe('Ionic Keyboard', function() { expect( keyboardHasPlugin() ).toEqual(true); }); - it('keyboardGetHeight() should = DEFAULT_KEYBOARD_HEIGHT if no plugin or resized view', function(){ + it('keyboardGetHeight() should use the keyboard plugin if it is available', function(){ + ionic.keyboard.height = 216; + expect( keyboardGetHeight() ).toEqual(216); + }); + + it('keyboardGetHeight() should = 275 if Cordova Android and is fullscreen', function(){ + ionic.Platform.setPlatform('android'); + window.cordova = {}; + ionic.Platform.isFullScreen = true; + expect( keyboardGetHeight() ).toEqual(275); }); + it('keyboardGetHeight() should = (keyboardViewportHeight - window.innerHeight) if Android and not fullscreen', function(){ + ionic.Platform.setPlatform('android'); + expect( ionic.Platform.isFullScreen ).toEqual(false); + + keyboardViewportHeight = 480; + window.innerHeight = 280 + + expect( keyboardGetHeight() ).toEqual(200); + }); + + it('keyboardGetHeight() should = 0 if keyboardViewportHeight = window.innerHeight and Android and not fullscreen', function(){ + ionic.Platform.setPlatform('android'); + expect( ionic.Platform.isFullScreen ).toEqual(false); + + keyboardViewportHeight = 480; + window.innerHeight = 480; + + expect( keyboardGetHeight() ).toEqual(0); + }); + + it('keyboardGetHeight() should = 206 if iOS and in landscape orientation', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.keyboard.landscape = true; + + expect( keyboardGetHeight() ).toEqual(206); + }) + + it('keyboardGetHeight() should = 216 if iOS Safari', function(){ + ionic.Platform.setPlatform('iOS'); + + expect( ionic.Platform.isWebView() ).toEqual(false); + expect( keyboardGetHeight() ).toEqual(216); + }) + + it('keyboardGetHeight() should = 260 if iOS Cordova', function(){ + ionic.Platform.setPlatform('iOS'); + window.cordova = {}; + + expect( ionic.Platform.isWebView() ).toEqual(true); + expect( keyboardGetHeight() ).toEqual(260); + }) + + it('keyboardGetHeight() should = 275 if not Android or iOS', function(){ + ionic.Platform.setPlatform('WP8'); + + expect( keyboardGetHeight() ).toEqual(275); + }) + it('keyboardUpdateViewportHeight() should update when window.innerHeight > keyboardViewportHeight', function(){ window.innerHeight = 460; keyboardViewportHeight = 320; @@ -171,34 +217,10 @@ describe('Ionic Keyboard', function() { var elementBottom = 200; var keyboardHeight = 200; var deviceHeight = 500; + window.innerHeight = 500; var details = keyboardShow(element, elementTop, elementBottom, deviceHeight, keyboardHeight); expect( details.isElementUnderKeyboard ).toEqual(false); }); - it('Should not subtract the keyboard height from the contentHeight if not keyboardIsOverWebView()', function(){ - var element = document.createElement('textarea'); - var elementTop = 300; - var elementBottom = 400; - var keyboardHeight = 200; - var deviceHeight = 260; - var details = keyboardShow(element, elementTop, elementBottom, deviceHeight, keyboardHeight); - - expect( details.contentHeight ).toEqual(260); - }); - - it('Should subtract the keyboard height from the contentHeight if keyboardIsOverWebView()', function(){ - ionic.Platform.setPlatform('iOS'); - ionic.Platform.setVersion('7.1'); - - var element = document.createElement('textarea'); - var elementTop = 300; - var elementBottom = 400; - var keyboardHeight = 200; - var deviceHeight = 568; - var details = keyboardShow(element, elementTop, elementBottom, deviceHeight, keyboardHeight); - - expect( details.contentHeight ).toEqual(368); - }); - }); diff --git a/test/unit/utils/viewport.unit.js b/test/unit/utils/viewport.unit.js index 3dd2902982..99b272fa45 100644 --- a/test/unit/utils/viewport.unit.js +++ b/test/unit/utils/viewport.unit.js @@ -12,10 +12,12 @@ iOS 6.1 Safari without viewport height DOES NOT resize NOTES: - -iOS 7.1 Safari with viewport height screws up ionic layout - -iOS 7.0 Safari with viewport height, the scroll view does not resize properly on keyboardhide - -iOS 7.0 Cordova without viewport height, scroll view does not resize properly switching inputs at bottom of page - -iOS 6.1 Cordova and Safari don't work well with viewport height + - iOS 7.1 Safari with viewport height screws up ionic layout + - iOS 7.0 Safari with viewport height, the scroll view does not resize properly on keyboardhide + - iOS 7.0 Cordova without viewport height, scroll view does not resize properly switching inputs at bottom of page + - iOS 6.1 Cordova and Safari don't work well with viewport height + - If its not a webview, and a viewport height was set, just removing + the height value doesn't trigger the change, but setting height=0 does the trick RECOMMENDATIONS: -iOS 7.1 Cordova no viewport height, keyboard is not over webview @@ -27,6 +29,9 @@ -iOS 6.1 Cordova no viewport height, keyboard is over webview -iOS 6.1 Safari no viewport height, keyboard is over webview + http://cordova.apache.org/docs/en/3.3.0/guide_platforms_ios_upgrading.md.html#Upgrading%20iOS + https://issues.apache.org/jira/browse/CB-4323 + */ @@ -40,6 +45,9 @@ describe('Ionic Viewport', function() { _activeElement = null; // the element which has focus window.cordova = undefined; window.device = undefined; + window.navigator = {}; + window.innerWidth = 1; + window.innerHeight = 2; ionic.Platform.ua = ''; ionic.Platform.platforms = null; ionic.Platform.setPlatform(''); @@ -57,69 +65,446 @@ describe('Ionic Viewport', function() { if(vportTag) vportTag.parentNode.removeChild(vportTag); }); - it('Should have height=device-height for iOS 7+ on webview', function(){ + + + // iOS 7.1, iPad, WebView + + it('Should remove width and height from viewport for iOS >= 7.1, iPad, WebView', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.1'); + window.cordova = {}; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no'); + }); + + + // iOS 7.1, iPad, Browser + + it('Should remove width and height from viewport for iOS >= 7.1, iPad, Browser', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.1'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no'); + }); + + + // iOS 7.0, iPad, WebView + + it('Should keep width and height in viewport for iOS 7.0, iPad, WebView, Portrait', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.0'); + window.cordova = {}; + window.innerWidth = 1; + window.innerHeight = 2; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=device-height'); + }); + + it('Should add width and height to viewport for iOS 7.0, iPad, WebView, Portrait', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.0'); + window.cordova = {}; + window.innerWidth = 1; + window.innerHeight = 2; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=device-height'); + }); + + it('Should add width and height=0 to viewport for iOS 7.0, iPad, WebView, Landscape', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.0'); + window.cordova = {}; + window.innerWidth = 2; + window.innerHeight = 1; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + it('Should keep width reset height to 0 in viewport for iOS 7.0, iPad, WebView, Landscape', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.0'); + window.cordova = {}; + window.innerWidth = 2; + window.innerHeight = 1; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + + // iOS 7.0, iPad, Browser + + it('Should keep width, but remove height from viewport for iOS 7.0, iPad, Browser', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.0'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should add width, but not add height to viewport for iOS 7.0, iPad, Browser', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('7.0'); + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + + // iOS 6.1, iPad, WebView + + it('Should keep width, but remove height from viewport for iOS 6.1, iPad, WebView, Portrait', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('6.1'); + window.cordova = {}; + window.innerWidth = 1; + window.innerHeight = 2; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should add width, but not add height to viewport for iOS 6.1, iPad, WebView, Portrait', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('6.1'); + window.cordova = {}; + window.innerWidth = 1; + window.innerHeight = 2; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should keep width, but replace height with 0 in viewport for iOS 6.1, iPad, WebView, Landscape', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('6.1'); + window.cordova = {}; + window.innerWidth = 2; + window.innerHeight = 1; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + it('Should add width and add height=0 to viewport for iOS 6.1, iPad, WebView, Landscape', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('6.1'); + window.cordova = {}; + window.innerWidth = 2; + window.innerHeight = 1; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + + // iOS 6.1, iPad, Browser + + it('Should keep width, and set height=0 for viewport for iOS 6.1, iPad, Browser, Portrait', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('6.1'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + it('Should add width, and add height=0 to viewport for iOS 6.1, iPad, Browser', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.ua = 'ipad'; + ionic.Platform.setVersion('6.1'); + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + + // iOS 7.1, iPhone, WebView + + it('Should remove width and height from viewport for iOS 7.1, iPhone, WebView', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.setVersion('7.1'); + window.cordova = {}; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no'); + }); + + it('Should do nothing width and height already in viewport for iOS 7.1, iPhone, WebView', function(){ + ionic.Platform.setPlatform('ios'); + ionic.Platform.setVersion('7.1'); + window.cordova = {}; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no'); + }); + + + // iOS 7.1, iPhone, Browser + + it('Should keep width, but remove height from viewport for iOS >= 7.1, iPhone, Browser', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('7.1'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should add width, but not height to viewport for iOS >= 7.1, iPhone, Browser', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('7.1'); + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + + // iOS 7.0, iPhone, WebView + + it('Should keep width, but not height in viewport for iOS 7.0, iPhone, WebView', function(){ ionic.Platform.setPlatform('iOS'); ionic.Platform.setVersion('7.0'); - expect( ionic.Platform.isAndroid() ).toEqual(false); - expect( ionic.Platform.isIOS() ).toEqual(true); - - //so isWebView() is true window.cordova = {}; + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; viewportLoadTag(); - expect( viewportProperties.height ).toEqual('device-height'); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); }); - it('Should not have height=device-height for iOS 7+ on browser', function(){ + it('Should add width, but not height to viewport for iOS 7.0, iPhone, WebView', function(){ ionic.Platform.setPlatform('iOS'); ionic.Platform.setVersion('7.0'); - expect( ionic.Platform.isAndroid() ).toEqual(false); - expect( ionic.Platform.isIOS() ).toEqual(true); - - viewportLoadTag(); - expect( viewportProperties.height ).not.toEqual('device-height'); - }); - - it('Should have height=device-height for Android on webview', function(){ - ionic.Platform.setPlatform('Android'); - expect( ionic.Platform.isAndroid() ).toEqual(true); - expect( ionic.Platform.isIOS() ).toEqual(false); - - //so isWebView() is true window.cordova = {}; + vportTag.content = 'user-scalable=no'; viewportLoadTag(); - expect( viewportProperties.height ).toEqual('device-height'); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); }); - it('Should not have height=device-height for Android on browser', function(){ - ionic.Platform.setPlatform('Android'); - expect( ionic.Platform.isAndroid() ).toEqual(true); - expect( ionic.Platform.isIOS() ).toEqual(false); + // iOS 7.0, iPhone, Browser + + it('Should keep width but remove height from viewport for iOS 7.0, iPhone, Browser', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('7.0'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; viewportLoadTag(); - expect( viewportProperties.height ).not.toEqual('device-height'); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); }); - it('Should not re-add height=device-height for webview if its already there', function(){ - ionic.Platform.setPlatform('ios'); + it('Should add width but not height to viewport for iOS 7.0, iPhone, Browser', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('7.0'); + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + + // iOS 6.1, iPhone, WebView + + it('Should add width but not height to viewport for iOS 6.1, iPhone, WebView', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('6.1'); window.cordova = {}; - var originalViewport = ' initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width, height=device-height '; - vportTag.setAttribute('content', originalViewport); + + vportTag.content = 'user-scalable=no'; viewportLoadTag(); - // if it was changed the spaces would have been removed - expect( vportTag.content ).toEqual(originalViewport); + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); }); - it('Should not update the viewport if its not a webview and height=device-height wasnt already in', function(){ - ionic.Platform.setPlatform('ios'); - var originalViewport = ' initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width '; - vportTag.setAttribute('content', originalViewport); + it('Should keep width but replace height=device-height with height=0 in viewport for iOS 6.1, iPhone, WebView', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('6.1'); + window.cordova = {}; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; viewportLoadTag(); - // if it was changed the spaces would have been removed - expect( vportTag.content ).toEqual(originalViewport); + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + + // iOS 6.1, iPhone, Browser + + it('Should add width but not height to viewport for iOS 6.1, iPhone, Browser', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('6.1'); + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should keep width but remove height from viewport for iOS 6.1, iPhone, Browser', function(){ + ionic.Platform.setPlatform('iOS'); + ionic.Platform.setVersion('6.1'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width, height=0'); + }); + + + // Android 4.4, WebView + + it('Should add width, but not height to viewport for Android 4.4, WebView', function(){ + ionic.Platform.setPlatform('android'); + ionic.Platform.setVersion('4.4'); + window.cordova = {}; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should keep width, but remove height from viewport for Android 4.4, WebView', function(){ + ionic.Platform.setPlatform('android'); + ionic.Platform.setVersion('4.4'); + window.cordova = {}; + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + + // Android 4.4, Browser + + it('Should add width, but not height to viewport for Android 4.4, Browser', function(){ + ionic.Platform.setPlatform('android'); + ionic.Platform.setVersion('4.4'); + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should keep width, but remove height from viewport for Android 4.4, Browser', function(){ + ionic.Platform.setPlatform('android'); + ionic.Platform.setVersion('4.4'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + + // Standard Browser + + it('Should add width, but not height to viewport for Standard Browser', function(){ + ionic.Platform.ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36'; + + vportTag.content = 'user-scalable=no'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + it('Should keep width, but remove height from viewport for Standard Browser', function(){ + ionic.Platform.setPlatform('android'); + ionic.Platform.setVersion('4.4'); + + vportTag.content = 'user-scalable=no, width=device-width, height=device-height'; + viewportLoadTag(); + + expect( vportTag.content ).toEqual('user-scalable=no, width=device-width'); + }); + + + + it('Should set viewport properties that have a key but without a value', function(){ + vportTag.content = ' keyonly, , ,, WIDTH=DeViCe-wIDTH , minimal-ui '; + viewportLoadTag(); + + expect( viewportProperties['keyonly'] ).toEqual('_'); + expect( viewportProperties['width'] ).toEqual('device-width'); + expect( viewportProperties['minimal-ui'] ).toEqual('_'); + + viewportTagUpdate(); + expect( vportTag.content ).toEqual('keyonly, width=device-width, minimal-ui'); + }); + + it('Should get portrait (0) orientation', function(){ + window.innerWidth = 768; + window.innerHeight = 1024; + expect( ionic.viewport.orientation() ).toEqual(0); + + window.innerWidth = 500; + window.innerHeight = 500; + expect( ionic.viewport.orientation() ).toEqual(0); + }); + + it('Should get landscape (90) orientation', function(){ + window.innerWidth = 1024; + window.innerHeight = 768; + expect( ionic.viewport.orientation() ).toEqual(90); }); }); diff --git a/test/unit/views/scrollView.unit.js b/test/unit/views/scrollView.unit.js index a6d77d7d3b..5c9ebd2014 100644 --- a/test/unit/views/scrollView.unit.js +++ b/test/unit/views/scrollView.unit.js @@ -37,19 +37,20 @@ describe('Scroll View', function() { it('Should resize when the keyboard is showing', function() { var element = document.createElement('textarea'); + ionic.Platform.setPlatform('ios'); s.appendChild(element); document.body.appendChild(sc); var sv = new ionic.views.Scroll({ el: sc, }); - + var scHeight = 500; sc.style.height = scHeight + "px"; sc.style.display = "block"; var keyboardHeight = 200; - details = { + details = { contentHeight: 260, elementBottom: 400, elementTop: 300,