viewporter.js 5.1 KB

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