mobile.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // TODO: This file was created by bulk-decaffeinate.
  2. // Sanity-check the conversion and remove this comment.
  3. /*
  4. * decaffeinate suggestions:
  5. * DS002: Fix invalid constructor
  6. * DS102: Remove unnecessary code created because of implicit returns
  7. * DS206: Consider reworking classes to avoid initClass
  8. * DS207: Consider shorter variations of null checks
  9. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
  10. */
  11. const Cls = (app.views.Mobile = class Mobile extends app.View {
  12. static initClass() {
  13. this.className = '_mobile';
  14. this.elements = {
  15. body: 'body',
  16. content: '._container',
  17. sidebar: '._sidebar',
  18. docPicker: '._settings ._sidebar'
  19. };
  20. this.shortcuts =
  21. {escape: 'onEscape'};
  22. this.routes =
  23. {after: 'afterRoute'};
  24. }
  25. static detect() {
  26. if (Cookies.get('override-mobile-detect') != null) {
  27. return JSON.parse(Cookies.get('override-mobile-detect'));
  28. }
  29. try {
  30. return (window.matchMedia('(max-width: 480px)').matches) ||
  31. (window.matchMedia('(max-width: 767px)').matches) ||
  32. (window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) ||
  33. // Need to sniff the user agent because some Android and Windows Phone devices don't take
  34. // resolution (dpi) into account when reporting device width/height.
  35. ((navigator.userAgent.indexOf('Android') !== -1) && (navigator.userAgent.indexOf('Mobile') !== -1)) ||
  36. (navigator.userAgent.indexOf('IEMobile') !== -1);
  37. } catch (error) {
  38. return false;
  39. }
  40. }
  41. static detectAndroidWebview() {
  42. try {
  43. return /(Android).*( Version\/.\.. ).*(Chrome)/.test(navigator.userAgent);
  44. } catch (error) {
  45. return false;
  46. }
  47. }
  48. constructor() {
  49. this.showSidebar = this.showSidebar.bind(this);
  50. this.hideSidebar = this.hideSidebar.bind(this);
  51. this.onClickBack = this.onClickBack.bind(this);
  52. this.onClickForward = this.onClickForward.bind(this);
  53. this.onClickToggleSidebar = this.onClickToggleSidebar.bind(this);
  54. this.onClickDocPickerTab = this.onClickDocPickerTab.bind(this);
  55. this.onClickSettingsTab = this.onClickSettingsTab.bind(this);
  56. this.onTapSearch = this.onTapSearch.bind(this);
  57. this.onEscape = this.onEscape.bind(this);
  58. this.afterRoute = this.afterRoute.bind(this);
  59. this.el = document.documentElement;
  60. super(...arguments);
  61. }
  62. init() {
  63. $.on($('._search'), 'touchend', this.onTapSearch);
  64. this.toggleSidebar = $('button[data-toggle-sidebar]');
  65. this.toggleSidebar.removeAttribute('hidden');
  66. $.on(this.toggleSidebar, 'click', this.onClickToggleSidebar);
  67. this.back = $('button[data-back]');
  68. this.back.removeAttribute('hidden');
  69. $.on(this.back, 'click', this.onClickBack);
  70. this.forward = $('button[data-forward]');
  71. this.forward.removeAttribute('hidden');
  72. $.on(this.forward, 'click', this.onClickForward);
  73. this.docPickerTab = $('button[data-tab="doc-picker"]');
  74. this.docPickerTab.removeAttribute('hidden');
  75. $.on(this.docPickerTab, 'click', this.onClickDocPickerTab);
  76. this.settingsTab = $('button[data-tab="settings"]');
  77. this.settingsTab.removeAttribute('hidden');
  78. $.on(this.settingsTab, 'click', this.onClickSettingsTab);
  79. app.document.sidebar.search
  80. .on('searching', this.showSidebar);
  81. this.activate();
  82. }
  83. showSidebar() {
  84. let selection;
  85. if (this.isSidebarShown()) {
  86. window.scrollTo(0, 0);
  87. return;
  88. }
  89. this.contentTop = window.scrollY;
  90. this.content.style.display = 'none';
  91. this.sidebar.style.display = 'block';
  92. if (selection = this.findByClass(app.views.ListSelect.activeClass)) {
  93. const scrollContainer = window.scrollY === this.body.scrollTop ? this.body : document.documentElement;
  94. $.scrollTo(selection, scrollContainer, 'center');
  95. } else {
  96. window.scrollTo(0, (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) || 0);
  97. }
  98. }
  99. hideSidebar() {
  100. if (!this.isSidebarShown()) { return; }
  101. this.sidebarTop = window.scrollY;
  102. this.sidebar.style.display = 'none';
  103. this.content.style.display = 'block';
  104. window.scrollTo(0, this.contentTop || 0);
  105. }
  106. isSidebarShown() {
  107. return this.sidebar.style.display !== 'none';
  108. }
  109. onClickBack() {
  110. return history.back();
  111. }
  112. onClickForward() {
  113. return history.forward();
  114. }
  115. onClickToggleSidebar() {
  116. if (this.isSidebarShown()) { this.hideSidebar(); } else { this.showSidebar(); }
  117. }
  118. onClickDocPickerTab(event) {
  119. $.stopEvent(event);
  120. this.showDocPicker();
  121. }
  122. onClickSettingsTab(event) {
  123. $.stopEvent(event);
  124. this.showSettings();
  125. }
  126. showDocPicker() {
  127. window.scrollTo(0, 0);
  128. this.docPickerTab.classList.add('active');
  129. this.settingsTab.classList.remove('active');
  130. this.docPicker.style.display = 'block';
  131. this.content.style.display = 'none';
  132. }
  133. showSettings() {
  134. window.scrollTo(0, 0);
  135. this.docPickerTab.classList.remove('active');
  136. this.settingsTab.classList.add('active');
  137. this.docPicker.style.display = 'none';
  138. this.content.style.display = 'block';
  139. }
  140. onTapSearch() {
  141. return window.scrollTo(0, 0);
  142. }
  143. onEscape() {
  144. return this.hideSidebar();
  145. }
  146. afterRoute(route) {
  147. this.hideSidebar();
  148. if (route === 'settings') {
  149. this.showDocPicker();
  150. } else {
  151. this.content.style.display = 'block';
  152. }
  153. if (page.canGoBack()) {
  154. this.back.removeAttribute('disabled');
  155. } else {
  156. this.back.setAttribute('disabled', 'disabled');
  157. }
  158. if (page.canGoForward()) {
  159. this.forward.removeAttribute('disabled');
  160. } else {
  161. this.forward.setAttribute('disabled', 'disabled');
  162. }
  163. }
  164. });
  165. Cls.initClass();