picker.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. var LocationPicker = {
  2. startZoom: 14,
  3. flySpeed: 2.4,
  4. notify: function(message) {
  5. if (window.external && window.external.invoke) {
  6. window.external.invoke(JSON.stringify(message));
  7. }
  8. },
  9. frameKeyDown: function (e) {
  10. const keyW = (e.key === 'w')
  11. || (e.code === 'KeyW')
  12. || (e.keyCode === 87);
  13. const keyQ = (e.key === 'q')
  14. || (e.code === 'KeyQ')
  15. || (e.keyCode === 81);
  16. const keyM = (e.key === 'm')
  17. || (e.code === 'KeyM')
  18. || (e.keyCode === 77);
  19. if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
  20. e.preventDefault();
  21. LocationPicker.notify({
  22. event: 'keydown',
  23. modifier: e.ctrlKey ? 'ctrl' : 'cmd',
  24. key: keyW ? 'w' : keyQ ? 'q' : 'm',
  25. });
  26. } else if (e.key === 'Escape' || e.keyCode === 27) {
  27. e.preventDefault();
  28. LocationPicker.notify({
  29. event: 'keydown',
  30. key: 'escape',
  31. });
  32. }
  33. },
  34. isNight: function() {
  35. var html = document.getElementsByTagName('html')[0];
  36. return html.style.getPropertyValue('--td-night') == '1';
  37. },
  38. lightPreset: function() {
  39. return LocationPicker.isNight() ? 'night' : 'day';
  40. },
  41. updateStyles: function (styles) {
  42. if (LocationPicker.styles !== styles) {
  43. LocationPicker.styles = styles;
  44. document.getElementsByTagName('html')[0].style = styles;
  45. LocationPicker.map.setConfigProperty(
  46. 'basemap',
  47. 'lightPreset',
  48. LocationPicker.lightPreset());
  49. }
  50. },
  51. init: function (params) {
  52. mapboxgl.accessToken = params.token;
  53. if (params.protocol) {
  54. mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
  55. }
  56. var options = { container: 'map', config: {
  57. basemap: { lightPreset: LocationPicker.lightPreset() }
  58. } };
  59. var center = params.center;
  60. if (center) {
  61. center = [center[1], center[0]];
  62. options.center = center;
  63. options.zoom = LocationPicker.startZoom;
  64. } else if (params.bounds) {
  65. options.bounds = params.bounds;
  66. center = new mapboxgl.LngLatBounds(params.bounds).getCenter();
  67. } else {
  68. center = [0, 0];
  69. }
  70. LocationPicker.map = new mapboxgl.Map(options);
  71. LocationPicker.createMarker(center);
  72. LocationPicker.trackMovement();
  73. LocationPicker.initSearchVenueRipple();
  74. },
  75. marker: function() {
  76. return document.getElementById('marker_drop');
  77. },
  78. createMarker: function(center) {
  79. document.getElementById('marker').style.display = 'flex';
  80. },
  81. clearMovingTimer: function() {
  82. if (LocationPicker.clearMovingTimeoutId) {
  83. clearTimeout(LocationPicker.clearMovingTimeoutId);
  84. LocationPicker.clearMovingTimeoutId = 0;
  85. }
  86. },
  87. startMovingTimer: function(done) {
  88. LocationPicker.clearMovingTimer();
  89. LocationPicker.clearMovingTimeoutId = setTimeout(done, 500);
  90. },
  91. trackMovement: function() {
  92. LocationPicker.map.on('movestart', function() {
  93. LocationPicker.marker().classList.add('moving');
  94. LocationPicker.clearMovingTimer();
  95. LocationPicker.toggleSearchVenues(false);
  96. LocationPicker.notify({ event: 'move_start' });
  97. });
  98. LocationPicker.map.on('moveend', function() {
  99. LocationPicker.startMovingTimer(function() {
  100. LocationPicker.marker().classList.remove('moving');
  101. LocationPicker.notify({
  102. event: 'move_end',
  103. latitude: LocationPicker.map.getCenter().lat,
  104. longitude: LocationPicker.map.getCenter().lng
  105. });
  106. });
  107. });
  108. },
  109. narrowTo: function (point) {
  110. LocationPicker.map.flyTo({
  111. center: [point[1], point[0]],
  112. zoom: LocationPicker.startZoom,
  113. speed: LocationPicker.flySpeed,
  114. });
  115. },
  116. send: function () {
  117. LocationPicker.notify({
  118. event: 'send',
  119. latitude: LocationPicker.map.getCenter().lat,
  120. longitude: LocationPicker.map.getCenter().lng
  121. });
  122. },
  123. addRipple: function (button, x, y) {
  124. const ripple = document.createElement('span');
  125. ripple.classList.add('ripple');
  126. const inner = document.createElement('span');
  127. inner.classList.add('inner');
  128. var rect = button.getBoundingClientRect();
  129. x -= rect.x;
  130. y -= rect.y;
  131. const mx = button.clientWidth - x;
  132. const my = button.clientHeight - y;
  133. const sq1 = x * x + y * y;
  134. const sq2 = mx * mx + y * y;
  135. const sq3 = x * x + my * my;
  136. const sq4 = mx * mx + my * my;
  137. const radius = Math.sqrt(Math.max(sq1, sq2, sq3, sq4));
  138. inner.style.width = inner.style.height = `${2 * radius}px`;
  139. inner.style.left = `${x - radius}px`;
  140. inner.style.top = `${y - radius}px`;
  141. inner.classList.add('inner');
  142. ripple.addEventListener('animationend', function (e) {
  143. if (e.animationName === 'fadeOut') {
  144. ripple.remove();
  145. }
  146. });
  147. ripple.appendChild(inner);
  148. button.appendChild(ripple);
  149. },
  150. stopRipples: function (button) {
  151. const id = button.id ? button.id : button;
  152. button = document.getElementById(id);
  153. const ripples = button.getElementsByClassName('ripple');
  154. for (var i = 0; i < ripples.length; ++i) {
  155. const ripple = ripples[i];
  156. if (!ripple.classList.contains('hiding')) {
  157. ripple.classList.add('hiding');
  158. }
  159. }
  160. },
  161. initSearchVenueRipple: function() {
  162. var button = document.getElementById('search_venues_inner');
  163. button.addEventListener('mousedown', function (e) {
  164. LocationPicker.addRipple(e.currentTarget, e.clientX, e.clientY);
  165. LocationPicker.searchVenuesPressed = true;
  166. });
  167. button.addEventListener('mouseup', function (e) {
  168. const id = e.currentTarget.id;
  169. setTimeout(function () {
  170. LocationPicker.stopRipples(id);
  171. }, 0);
  172. if (LocationPicker.searchVenuesPressed) {
  173. LocationPicker.searchVenuesPressed = false;
  174. LocationPicker.toggleSearchVenues(false);
  175. LocationPicker.notify({
  176. event: 'search_venues',
  177. latitude: LocationPicker.map.getCenter().lat,
  178. longitude: LocationPicker.map.getCenter().lng
  179. });
  180. }
  181. });
  182. button.addEventListener('mouseleave', function (e) {
  183. LocationPicker.stopRipples(e.currentTarget);
  184. LocationPicker.searchVenuesPressed = false;
  185. });
  186. },
  187. toggleSearchVenues: function(shown) {
  188. var button = document.getElementById('search_venues');
  189. button.classList.toggle('shown', shown);
  190. },
  191. };