viewporter.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * Viewporter v2.0
  3. * http://github.com/zynga/viewporter
  4. *
  5. * Copyright 2011, Zynga Inc.
  6. * Licensed under the MIT License.
  7. * https://raw.github.com/zynga/viewporter/master/MIT-LICENSE.txt
  8. */
  9. var viewporter;
  10. (function() {
  11. var _viewporter;
  12. // initialize viewporter object
  13. viewporter = {
  14. // options
  15. forceDetection: false,
  16. disableLegacyAndroid: true,
  17. // constants
  18. ACTIVE: (function() {
  19. // it's best not do to anything to very weak devices running Android 2.x
  20. if(viewporter.disableLegacyAndroid && (/android 2/i).test(navigator.userAgent)) {
  21. return false;
  22. }
  23. // iPad's don't allow you to scroll away the UI of the browser
  24. if((/ipad/i).test(navigator.userAgent)) {
  25. return false;
  26. }
  27. // WebOS has no touch events, but definitely the need for viewport normalization
  28. if((/webos/i).test(navigator.userAgent)) {
  29. return true;
  30. }
  31. // touch enabled devices
  32. if('ontouchstart' in window) {
  33. return true;
  34. }
  35. return false;
  36. }),
  37. READY: false,
  38. // methods
  39. isLandscape: function() {
  40. return window.orientation === 90 || window.orientation === -90;
  41. },
  42. ready: function(callback) {
  43. window.addEventListener('viewportready', callback, false);
  44. },
  45. change: function(callback) {
  46. window.addEventListener('viewportchange', callback, false);
  47. },
  48. refresh: function(){
  49. if (_viewporter) {
  50. _viewporter.prepareVisualViewport();
  51. }
  52. },
  53. preventPageScroll: function() {
  54. // prevent page scroll if `preventPageScroll` option was set to `true`
  55. document.body.addEventListener('touchmove', function(event) {
  56. event.preventDefault();
  57. }, false);
  58. // reset page scroll if `preventPageScroll` option was set to `true`
  59. // this is used after showing the address bar on iOS
  60. document.body.addEventListener("touchstart", function() {
  61. _viewporter.prepareVisualViewport();
  62. }, false);
  63. }
  64. };
  65. // execute the ACTIVE flag
  66. viewporter.ACTIVE = viewporter.ACTIVE();
  67. // if we are on Desktop, no need to go further
  68. if (!viewporter.ACTIVE) {
  69. return;
  70. }
  71. // create private constructor with prototype..just looks cooler
  72. var _Viewporter = function() {
  73. var that = this;
  74. // Scroll away the header, but not in Chrome
  75. this.IS_ANDROID = /Android/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
  76. var _onReady = function() {
  77. // scroll the shit away and fix the viewport!
  78. that.prepareVisualViewport();
  79. // listen for orientation change
  80. var cachedOrientation = window.orientation;
  81. window.addEventListener('orientationchange', function() {
  82. if(window.orientation !== cachedOrientation) {
  83. that.prepareVisualViewport();
  84. cachedOrientation = window.orientation;
  85. }
  86. }, false);
  87. };
  88. // listen for document ready if not already loaded
  89. // then try to prepare the visual viewport and start firing custom events
  90. if (document.readyState === 'loading') {
  91. document.addEventListener('DOMContentLoaded', function() {
  92. _onReady();
  93. }, false);
  94. } else {
  95. _onReady();
  96. }
  97. };
  98. _Viewporter.prototype = {
  99. getProfile: function() {
  100. if(viewporter.forceDetection) {
  101. return null;
  102. }
  103. for(var searchTerm in viewporter.profiles) {
  104. if(new RegExp(searchTerm).test(navigator.userAgent)) {
  105. return viewporter.profiles[searchTerm];
  106. }
  107. }
  108. return null;
  109. },
  110. postProcess: function() {
  111. // let everyone know we're finally ready
  112. viewporter.READY = true;
  113. this.triggerWindowEvent(!this._firstUpdateExecuted ? 'viewportready' : 'viewportchange');
  114. this._firstUpdateExecuted = true;
  115. },
  116. prepareVisualViewport: function() {
  117. var that = this;
  118. // if we're running in webapp mode (iOS), there's nothing to scroll away
  119. if(navigator.standalone) {
  120. return this.postProcess();
  121. }
  122. // maximize the document element's height to be able to scroll away the url bar
  123. document.documentElement.style.minHeight = '5000px';
  124. var startHeight = window.innerHeight;
  125. var deviceProfile = this.getProfile();
  126. var orientation = viewporter.isLandscape() ? 'landscape' : 'portrait';
  127. // try scrolling immediately
  128. window.scrollTo(0, that.IS_ANDROID ? 1 : 0); // Android needs to scroll by at least 1px
  129. // start the checker loop
  130. var iterations = 40;
  131. var check = window.setInterval(function() {
  132. // retry scrolling
  133. window.scrollTo(0, that.IS_ANDROID ? 1 : 0); // Android needs to scroll by at least 1px
  134. function androidProfileCheck() {
  135. return deviceProfile ? window.innerHeight === deviceProfile[orientation] : false;
  136. }
  137. function iosInnerHeightCheck() {
  138. return window.innerHeight > startHeight;
  139. }
  140. iterations--;
  141. // check iterations first to make sure we never get stuck
  142. if ( (that.IS_ANDROID ? androidProfileCheck() : iosInnerHeightCheck()) || iterations < 0) {
  143. // set minimum height of content to new window height
  144. document.documentElement.style.minHeight = window.innerHeight + 'px';
  145. // set the right height for the body wrapper to allow bottom positioned elements
  146. document.getElementById('viewporter').style.position = 'relative';
  147. document.getElementById('viewporter').style.height = window.innerHeight + 'px';
  148. clearInterval(check);
  149. // fire events, get ready
  150. that.postProcess();
  151. }
  152. }, 10);
  153. },
  154. triggerWindowEvent: function(name) {
  155. var event = document.createEvent("Event");
  156. event.initEvent(name, false, false);
  157. window.dispatchEvent(event);
  158. }
  159. };
  160. // initialize
  161. _viewporter = new _Viewporter();
  162. })();
  163. viewporter.profiles = {
  164. // Motorola Xoom
  165. 'MZ601': {
  166. portrait: 696,
  167. landscape: 1176
  168. },
  169. // Samsung Galaxy S, S2 and Nexus S
  170. 'GT-I9000|GT-I9100|Nexus S': {
  171. portrait: 508,
  172. landscape: 295
  173. },
  174. // Samsung Galaxy Pad
  175. 'GT-P1000': {
  176. portrait: 657,
  177. landscape: 400
  178. },
  179. // HTC Desire & HTC Desire HD
  180. 'Desire_A8181|DesireHD_A9191': {
  181. portrait: 533,
  182. landscape: 320
  183. }
  184. };