1. 1 : /* global log -- eslint */
  2. 2 :
  3. 3 : /**
  4. 4 : * @file This file provides functions and utilities specifically for the smartphone layout of IITC.
  5. 5 : * @module smartphone
  6. 6 : */
  7. 7 :
  8. 8 : /**
  9. 9 : * Determines if the user's device is a smartphone.
  10. 10 : * Note it should not detect tablets because their display is large enough to use the desktop version.
  11. 11 : * The stock intel site allows forcing mobile/full sites with a vp=m or vp=f parameter. This function supports the same.
  12. 12 : *
  13. 13 : * @function isSmartphone
  14. 14 : * @returns {boolean} True if the user's device is a smartphone, false otherwise.
  15. 15 : */
  16. 16 : window.isSmartphone = function () {
  17. 17 : // this check is also used in main.js. Note it should not detect
  18. 18 : // tablets because their display is large enough to use the desktop
  19. 19 : // version.
  20. 20 :
  21. 21 : // The stock intel site allows forcing mobile/full sites with a vp=m or vp=f
  22. 22 : // parameter - let's support the same. (stock only allows this for some
  23. 23 : // browsers - e.g. android phone/tablet. let's allow it for all, but
  24. 24 : // no promises it'll work right)
  25. 25 : var viewParam = window.getURLParam('vp');
  26. 26 : if (viewParam === 'm') return true;
  27. 27 : if (viewParam === 'f') return false;
  28. 28 :
  29. 29 : return !!(navigator.userAgent.match(/Android.*Mobile/) || navigator.userAgent.match(/iPhone|iPad|iPod/i));
  30. 30 : };
  31. 31 :
  32. 32 : /**
  33. 33 : * Placeholder for smartphone specific manipulations.
  34. 34 : * This function does not implement any logic by itself.
  35. 35 : *
  36. 36 : * @function smartphone
  37. 37 : */
  38. 38 : window.smartphone = function () {};
  39. 39 :
  40. 40 : /**
  41. 41 : * Performs initial setup tasks for IITC on smartphones before the IITC boot process.
  42. 42 : * This includes adding smartphone-specific stylesheets
  43. 43 : * and modifying some of the setup functions for mobile compatibility.
  44. 44 : *
  45. 45 : * @function runOnSmartphonesBeforeBoot
  46. 46 : */
  47. 47 : window.runOnSmartphonesBeforeBoot = function () {
  48. 48 : if (!window.isSmartphone()) return;
  49. 49 : log.warn('running smartphone pre boot stuff');
  50. 50 :
  51. 51 : // add smartphone stylesheet
  52. 52 : var style = document.createElement('style');
  53. 53 : style.type = 'text/css';
  54. 54 : style.appendChild(document.createTextNode('@include_string:smartphone.css@'));
  55. 55 : document.head.appendChild(style);
  56. 56 :
  57. 57 : // don’t need many of those
  58. 58 : window.setupStyles = function () {
  59. 59 : $('head').append(
  60. 60 : '<style>' +
  61. 61 : [
  62. 62 : '#largepreview.enl img { border:2px solid ' + window.COLORS[window.TEAM_ENL] + '; } ',
  63. 63 : '#largepreview.res img { border:2px solid ' + window.COLORS[window.TEAM_RES] + '; } ',
  64. 64 : '#largepreview.none img { border:2px solid ' + window.COLORS[window.TEAM_NONE] + '; } ',
  65. 65 : ].join('\n') +
  66. 66 : '</style>'
  67. 67 : );
  68. 68 : };
  69. 69 :
  70. 70 : window.smartphone.mapButton = $('<a>map</a>').click(function () {
  71. 71 : window.show('map');
  72. 72 : $('#map').css({ visibility: 'visible', opacity: '1' });
  73. 73 : $('#updatestatus').show();
  74. 74 : $('#chatcontrols a.active').removeClass('active');
  75. 75 : $("#chatcontrols a:contains('map')").addClass('active');
  76. 76 : });
  77. 77 :
  78. 78 : window.smartphone.sideButton = $('<a>info</a>').click(function () {
  79. 79 : window.show('info');
  80. 80 : $('#scrollwrapper').show();
  81. 81 : window.resetScrollOnNewPortal();
  82. 82 : $('#chatcontrols a.active').removeClass('active');
  83. 83 : $("#chatcontrols a:contains('info')").addClass('active');
  84. 84 : });
  85. 85 :
  86. 86 : $('#chatcontrols').append(window.smartphone.mapButton).append(window.smartphone.sideButton);
  87. 87 :
  88. 88 : if (!window.useAppPanes()) {
  89. 89 : document.body.classList.add('show_controls');
  90. 90 : }
  91. 91 :
  92. 92 : window.addHook('portalDetailsUpdated', function () {
  93. 93 : var x = $('.imgpreview img').removeClass('hide');
  94. 94 :
  95. 95 : if (!x.length) {
  96. 96 : $('.fullimg').remove();
  97. 97 : return;
  98. 98 : }
  99. 99 :
  100. 100 : if ($('.fullimg').length) {
  101. 101 : $('.fullimg').replaceWith(x.addClass('fullimg'));
  102. 102 : } else {
  103. 103 : x.addClass('fullimg').appendTo('#sidebar');
  104. 104 : }
  105. 105 : });
  106. 106 : };
  107. 107 :
  108. 108 : /**
  109. 109 : * Updates the mobile information bar with portal details when a portal is selected.
  110. 110 : * This function is hooked to the 'portalSelected' event and is specific to the smartphone layout.
  111. 111 : *
  112. 112 : * @function smartphoneInfo
  113. 113 : * @param {Object} selectedPortalData - The object containing details about the selected portal.
  114. 114 : */
  115. 115 : window.smartphoneInfo = function (selectedPortalData) {
  116. 116 : var guid = selectedPortalData.selectedPortalGuid;
  117. 117 : if (!window.portals[guid]) return;
  118. 118 :
  119. 119 : var data = window.portals[window.selectedPortal].options.data;
  120. 120 : if (typeof data.title === 'undefined') return;
  121. 121 :
  122. 122 : var details = window.portalDetail.get(guid);
  123. 123 :
  124. 124 : var lvl = data.level;
  125. 125 : let t;
  126. 126 : if (data.team === 'N' || data.team === 'NEUTRAL') t = '<span class="portallevel">L0</span>';
  127. 127 : else t = '<span class="portallevel" style="background: ' + window.COLORS_LVL[lvl] + ';">L' + lvl + '</span>';
  128. 128 :
  129. 129 : var percentage = data.health;
  130. 130 : if (details) {
  131. 131 : var totalEnergy = window.getTotalPortalEnergy(details);
  132. 132 : if (window.getTotalPortalEnergy(details) > 0) {
  133. 133 : percentage = Math.floor((window.getCurrentPortalEnergy(details) / totalEnergy) * 100);
  134. 134 : }
  135. 135 : }
  136. 136 : t += ' ' + percentage + '% ';
  137. 137 : t += data.title;
  138. 138 :
  139. 139 : if (details) {
  140. 140 : var l, v, max, perc;
  141. 141 : var eastAnticlockwiseToNorthClockwise = [2, 1, 0, 7, 6, 5, 4, 3];
  142. 142 :
  143. 143 : for (var ind = 0; ind < 8; ind++) {
  144. 144 : let slot, reso;
  145. 145 : if (details.resonators.length === 8) {
  146. 146 : slot = eastAnticlockwiseToNorthClockwise[ind];
  147. 147 : reso = details.resonators[slot];
  148. 148 : } else {
  149. 149 : slot = null;
  150. 150 : reso = ind < details.resonators.length ? details.resonators[ind] : null;
  151. 151 : }
  152. 152 :
  153. 153 : var className = window.TEAM_TO_CSS[window.getTeam(details)];
  154. 154 : if (slot !== null && window.OCTANTS[slot] === 'N') className += ' north';
  155. 155 : if (reso) {
  156. 156 : l = parseInt(reso.level);
  157. 157 : v = parseInt(reso.energy);
  158. 158 : max = window.RESO_NRG[l];
  159. 159 : perc = (v / max) * 100;
  160. 160 : } else {
  161. 161 : l = 0;
  162. 162 : v = 0;
  163. 163 : max = 0;
  164. 164 : perc = 0;
  165. 165 : }
  166. 166 :
  167. 167 : t += '<div class="resonator ' + className + '" style="border-top-color: ' + window.COLORS_LVL[l] + ';left: ' + (100 * ind) / 8.0 + '%;">';
  168. 168 : t += '<div class="filllevel" style="width:' + perc + '%;"></div>';
  169. 169 : t += '</div>';
  170. 170 : }
  171. 171 : }
  172. 172 :
  173. 173 : $('#mobileinfo').html(t);
  174. 174 : };
  175. 175 :
  176. 176 : /**
  177. 177 : * Performs setup tasks for IITC on smartphones after the IITC boot process.
  178. 178 : * This includes initializing mobile info display, adjusting UI elements for mobile compatibility,
  179. 179 : * and setting event handlers for mobile-specific interactions.
  180. 180 : *
  181. 181 : * @function runOnSmartphonesAfterBoot
  182. 182 : */
  183. 183 : window.runOnSmartphonesAfterBoot = function () {
  184. 184 : if (!window.isSmartphone()) return;
  185. 185 : log.warn('running smartphone post boot stuff');
  186. 186 :
  187. 187 : window.show('map');
  188. 188 :
  189. 189 : // add a div/hook for updating mobile info
  190. 190 : $('#updatestatus').prepend('<div id="mobileinfo" onclick="show(\'info\')"></div>');
  191. 191 : window.addHook('portalSelected', window.smartphoneInfo);
  192. 192 : window.addHook('portalDetailLoaded', (data) => {
  193. 193 : if (data.success && data.guid === window.selectedPortal) {
  194. 194 : window.smartphoneInfo({ selectedPortalGuid: data.guid });
  195. 195 : }
  196. 196 : });
  197. 197 :
  198. 198 : // init msg of status bar. hint for the user that a tap leads to the info screen
  199. 199 : $('#mobileinfo').html('<div style="text-align: center"><b>tap here for info screen</b></div>');
  200. 200 :
  201. 201 : // replace img full view handler
  202. 202 : $('#portaldetails')
  203. 203 : .off('click', '.imgpreview')
  204. 204 : .on('click', '.imgpreview', function (e) {
  205. 205 : if (e.currentTarget === e.target) {
  206. 206 : // do not fire on #level
  207. 207 : $('.ui-tooltip').remove();
  208. 208 : var newTop = $('.fullimg').position().top + $('#sidebar').scrollTop();
  209. 209 : $('#sidebar').animate({ scrollTop: newTop }, 200);
  210. 210 : }
  211. 211 : });
  212. 212 : };