@tak-ps/cloudtak 12.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/README.md +34 -0
  2. package/dist/types/plugin.d.ts +99 -0
  3. package/dist/types/src/App.vue.d.ts +5 -0
  4. package/dist/types/src/base/chatroom-chats.d.ts +17 -0
  5. package/dist/types/src/base/chatroom.d.ts +47 -0
  6. package/dist/types/src/base/cot.d.ts +94 -0
  7. package/dist/types/src/base/database.d.ts +115 -0
  8. package/dist/types/src/base/events.d.ts +30 -0
  9. package/dist/types/src/base/filter.d.ts +41 -0
  10. package/dist/types/src/base/handler.d.ts +11 -0
  11. package/dist/types/src/base/icon.d.ts +4 -0
  12. package/dist/types/src/base/notification.d.ts +37 -0
  13. package/dist/types/src/base/overlay.d.ts +94 -0
  14. package/dist/types/src/base/subscription-feature.d.ts +49 -0
  15. package/dist/types/src/base/subscription-log.d.ts +32 -0
  16. package/dist/types/src/base/subscription.d.ts +115 -0
  17. package/dist/types/src/base/utils/styles.d.ts +11 -0
  18. package/dist/types/src/base/validators.d.ts +9 -0
  19. package/dist/types/src/components/Admin/AdminImports.vue.d.ts +3 -0
  20. package/dist/types/src/components/Admin/AdminLayers.vue.d.ts +3 -0
  21. package/dist/types/src/components/Admin/AdminMissionTemplate.vue.d.ts +3 -0
  22. package/dist/types/src/components/Admin/AdminMissionTemplateLog.vue.d.ts +3 -0
  23. package/dist/types/src/components/Admin/AdminMissionTemplates.vue.d.ts +3 -0
  24. package/dist/types/src/components/Admin/AdminOverlays.vue.d.ts +3 -0
  25. package/dist/types/src/components/Admin/AdminPalette.vue.d.ts +3 -0
  26. package/dist/types/src/components/Admin/AdminPaletteFeature.vue.d.ts +3 -0
  27. package/dist/types/src/components/Admin/AdminPalettes.vue.d.ts +3 -0
  28. package/dist/types/src/components/Admin/AdminServer.vue.d.ts +3 -0
  29. package/dist/types/src/components/Admin/AdminTasks.vue.d.ts +3 -0
  30. package/dist/types/src/components/Admin/AdminUser.vue.d.ts +3 -0
  31. package/dist/types/src/components/Admin/AdminUsers.vue.d.ts +3 -0
  32. package/dist/types/src/components/Admin/AdminVideos.vue.d.ts +3 -0
  33. package/dist/types/src/components/Admin/Server/ServerConnection.vue.d.ts +3 -0
  34. package/dist/types/src/components/Admin/Server/ServerInjectorModal.vue.d.ts +11 -0
  35. package/dist/types/src/components/Admin/Server/ServerInjectors.vue.d.ts +3 -0
  36. package/dist/types/src/components/Admin/Server/ServerPackages.vue.d.ts +3 -0
  37. package/dist/types/src/components/Admin/Server/ServerRepeaters.vue.d.ts +3 -0
  38. package/dist/types/src/components/Admin/Server/ServerVideos.vue.d.ts +3 -0
  39. package/dist/types/src/components/Admin/Tasks/AdminRawTasks.vue.d.ts +3 -0
  40. package/dist/types/src/components/Admin/Videos/AdminVideoLeases.vue.d.ts +3 -0
  41. package/dist/types/src/components/Admin/Videos/AdminVideoService.vue.d.ts +3 -0
  42. package/dist/types/src/components/Admin/Videos/VideoConfig.vue.d.ts +7 -0
  43. package/dist/types/src/components/Admin/Videos/VideoConfigPort.vue.d.ts +8 -0
  44. package/dist/types/src/components/CloudTAK/CoTView.vue.d.ts +3 -0
  45. package/dist/types/src/components/CloudTAK/CoordInput.vue.d.ts +7 -0
  46. package/dist/types/src/components/CloudTAK/DrawTools.vue.d.ts +3 -0
  47. package/dist/types/src/components/CloudTAK/FeatView.vue.d.ts +8 -0
  48. package/dist/types/src/components/CloudTAK/GeoJSONInput.vue.d.ts +24 -0
  49. package/dist/types/src/components/CloudTAK/MainMenu.vue.d.ts +9 -0
  50. package/dist/types/src/components/CloudTAK/MainMenuContents.vue.d.ts +16 -0
  51. package/dist/types/src/components/CloudTAK/Map.vue.d.ts +8 -0
  52. package/dist/types/src/components/CloudTAK/MapLoading.vue.d.ts +3 -0
  53. package/dist/types/src/components/CloudTAK/Menu/Debugger.vue.d.ts +3 -0
  54. package/dist/types/src/components/CloudTAK/Menu/MenuBasemaps.vue.d.ts +3 -0
  55. package/dist/types/src/components/CloudTAK/Menu/MenuChannels.vue.d.ts +3 -0
  56. package/dist/types/src/components/CloudTAK/Menu/MenuChats.vue.d.ts +3 -0
  57. package/dist/types/src/components/CloudTAK/Menu/MenuConnections.vue.d.ts +3 -0
  58. package/dist/types/src/components/CloudTAK/Menu/MenuContacts.vue.d.ts +3 -0
  59. package/dist/types/src/components/CloudTAK/Menu/MenuFeatures.vue.d.ts +3 -0
  60. package/dist/types/src/components/CloudTAK/Menu/MenuFeaturesDeleted.vue.d.ts +3 -0
  61. package/dist/types/src/components/CloudTAK/Menu/MenuFiles.vue.d.ts +3 -0
  62. package/dist/types/src/components/CloudTAK/Menu/MenuImport.vue.d.ts +3 -0
  63. package/dist/types/src/components/CloudTAK/Menu/MenuImports.vue.d.ts +3 -0
  64. package/dist/types/src/components/CloudTAK/Menu/MenuItemCard.vue.d.ts +101 -0
  65. package/dist/types/src/components/CloudTAK/Menu/MenuMission.vue.d.ts +3 -0
  66. package/dist/types/src/components/CloudTAK/Menu/MenuMissions.vue.d.ts +3 -0
  67. package/dist/types/src/components/CloudTAK/Menu/MenuOverlayExplorer.vue.d.ts +3 -0
  68. package/dist/types/src/components/CloudTAK/Menu/MenuOverlays.vue.d.ts +3 -0
  69. package/dist/types/src/components/CloudTAK/Menu/MenuPackage.vue.d.ts +3 -0
  70. package/dist/types/src/components/CloudTAK/Menu/MenuPackages.vue.d.ts +3 -0
  71. package/dist/types/src/components/CloudTAK/Menu/MenuRoutes.vue.d.ts +3 -0
  72. package/dist/types/src/components/CloudTAK/Menu/MenuRoutesNew.vue.d.ts +3 -0
  73. package/dist/types/src/components/CloudTAK/Menu/MenuSettings.vue.d.ts +3 -0
  74. package/dist/types/src/components/CloudTAK/Menu/MenuSettingsCallsign.vue.d.ts +3 -0
  75. package/dist/types/src/components/CloudTAK/Menu/MenuSettingsDisplay.vue.d.ts +3 -0
  76. package/dist/types/src/components/CloudTAK/Menu/MenuVideos.vue.d.ts +3 -0
  77. package/dist/types/src/components/CloudTAK/Menu/MenuVideosRemote.vue.d.ts +3 -0
  78. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionContents.vue.d.ts +11 -0
  79. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionCreate.vue.d.ts +7 -0
  80. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionInfo.vue.d.ts +11 -0
  81. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionInviteModal.vue.d.ts +17 -0
  82. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionLayerCreate.vue.d.ts +13 -0
  83. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionLayerEdit.vue.d.ts +15 -0
  84. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionLayerTree.vue.d.ts +15 -0
  85. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionLayers.vue.d.ts +8 -0
  86. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionLog.vue.d.ts +9 -0
  87. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionLogs.vue.d.ts +7 -0
  88. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionTimeline.vue.d.ts +7 -0
  89. package/dist/types/src/components/CloudTAK/Menu/Mission/MissionUsers.vue.d.ts +7 -0
  90. package/dist/types/src/components/CloudTAK/Menu/Mission/PendingInvites.vue.d.ts +11 -0
  91. package/dist/types/src/components/CloudTAK/Menu/Overlays/TreeMission.vue.d.ts +7 -0
  92. package/dist/types/src/components/CloudTAK/Menu/Overlays/TreeVector.vue.d.ts +13 -0
  93. package/dist/types/src/components/CloudTAK/Menu/Videos/VideoLeaseModal.vue.d.ts +16 -0
  94. package/dist/types/src/components/CloudTAK/Menu/Videos/VideosRemoteFeed.vue.d.ts +13 -0
  95. package/dist/types/src/components/CloudTAK/Notifications.vue.d.ts +3 -0
  96. package/dist/types/src/components/CloudTAK/Query/Magnetic.vue.d.ts +7 -0
  97. package/dist/types/src/components/CloudTAK/Query/Reverse.vue.d.ts +8 -0
  98. package/dist/types/src/components/CloudTAK/Query/Sun.vue.d.ts +7 -0
  99. package/dist/types/src/components/CloudTAK/Query/Weather.vue.d.ts +7 -0
  100. package/dist/types/src/components/CloudTAK/QueryView.vue.d.ts +3 -0
  101. package/dist/types/src/components/CloudTAK/RangeInput.vue.d.ts +7 -0
  102. package/dist/types/src/components/CloudTAK/RangeRingsInput.vue.d.ts +7 -0
  103. package/dist/types/src/components/CloudTAK/util/Breadcrumb.vue.d.ts +6 -0
  104. package/dist/types/src/components/CloudTAK/util/ChannelInfo.vue.d.ts +15 -0
  105. package/dist/types/src/components/CloudTAK/util/ContactPuck.vue.d.ts +7 -0
  106. package/dist/types/src/components/CloudTAK/util/Coordinate.vue.d.ts +67 -0
  107. package/dist/types/src/components/CloudTAK/util/CoordinateType.vue.d.ts +27 -0
  108. package/dist/types/src/components/CloudTAK/util/CopyButton.vue.d.ts +32 -0
  109. package/dist/types/src/components/CloudTAK/util/CopyField.vue.d.ts +122 -0
  110. package/dist/types/src/components/CloudTAK/util/DrawOverlay.vue.d.ts +3 -0
  111. package/dist/types/src/components/CloudTAK/util/EmptyInfo.vue.d.ts +24 -0
  112. package/dist/types/src/components/CloudTAK/util/FeatureIcon.vue.d.ts +23 -0
  113. package/dist/types/src/components/CloudTAK/util/FeatureRow.vue.d.ts +81 -0
  114. package/dist/types/src/components/CloudTAK/util/FloatingAttachment.vue.d.ts +17 -0
  115. package/dist/types/src/components/CloudTAK/util/FloatingVideo.vue.d.ts +27 -0
  116. package/dist/types/src/components/CloudTAK/util/GroupSelect.vue.d.ts +14 -0
  117. package/dist/types/src/components/CloudTAK/util/JSONModal.vue.d.ts +27 -0
  118. package/dist/types/src/components/CloudTAK/util/Keywords.vue.d.ts +6 -0
  119. package/dist/types/src/components/CloudTAK/util/LineLength.vue.d.ts +24 -0
  120. package/dist/types/src/components/CloudTAK/util/MenuTemplate.vue.d.ts +71 -0
  121. package/dist/types/src/components/CloudTAK/util/MultipleSelect.vue.d.ts +7 -0
  122. package/dist/types/src/components/CloudTAK/util/NotificationIcon.vue.d.ts +10 -0
  123. package/dist/types/src/components/CloudTAK/util/NotificationToast.vue.d.ts +13 -0
  124. package/dist/types/src/components/CloudTAK/util/PolygonArea.vue.d.ts +24 -0
  125. package/dist/types/src/components/CloudTAK/util/PropertyBattery.vue.d.ts +13 -0
  126. package/dist/types/src/components/CloudTAK/util/PropertyBearing.vue.d.ts +58 -0
  127. package/dist/types/src/components/CloudTAK/util/PropertyCreator.vue.d.ts +7 -0
  128. package/dist/types/src/components/CloudTAK/util/PropertyDistance.vue.d.ts +54 -0
  129. package/dist/types/src/components/CloudTAK/util/PropertyElevation.vue.d.ts +32 -0
  130. package/dist/types/src/components/CloudTAK/util/PropertyEmail.vue.d.ts +13 -0
  131. package/dist/types/src/components/CloudTAK/util/PropertyLinks.vue.d.ts +14 -0
  132. package/dist/types/src/components/CloudTAK/util/PropertyMetadata.vue.d.ts +7 -0
  133. package/dist/types/src/components/CloudTAK/util/PropertyMilSym.vue.d.ts +25 -0
  134. package/dist/types/src/components/CloudTAK/util/PropertyPhone.vue.d.ts +13 -0
  135. package/dist/types/src/components/CloudTAK/util/PropertySpeed.vue.d.ts +23 -0
  136. package/dist/types/src/components/CloudTAK/util/PropertyStyle.vue.d.ts +7 -0
  137. package/dist/types/src/components/CloudTAK/util/PropertyTimes.vue.d.ts +7 -0
  138. package/dist/types/src/components/CloudTAK/util/PropertyType.vue.d.ts +27 -0
  139. package/dist/types/src/components/CloudTAK/util/SearchBox.vue.d.ts +37 -0
  140. package/dist/types/src/components/CloudTAK/util/SelectFeats.vue.d.ts +7 -0
  141. package/dist/types/src/components/CloudTAK/util/SettingsCallsign.vue.d.ts +28 -0
  142. package/dist/types/src/components/CloudTAK/util/Share.vue.d.ts +14 -0
  143. package/dist/types/src/components/CloudTAK/util/ShareToMission.vue.d.ts +53 -0
  144. package/dist/types/src/components/CloudTAK/util/ShareToPackage.vue.d.ts +62 -0
  145. package/dist/types/src/components/CloudTAK/util/SlideDownHeader.vue.d.ts +41 -0
  146. package/dist/types/src/components/CloudTAK/util/StandardItem.vue.d.ts +18 -0
  147. package/dist/types/src/components/CloudTAK/util/Subscriptions.vue.d.ts +7 -0
  148. package/dist/types/src/components/CloudTAK/util/VideoLeaseSourceType.vue.d.ts +6 -0
  149. package/dist/types/src/components/CloudTAK/util/WarnChannels.vue.d.ts +7 -0
  150. package/dist/types/src/components/CloudTAK/util/WarnConfiguration.vue.d.ts +7 -0
  151. package/dist/types/src/components/Configure.vue.d.ts +3 -0
  152. package/dist/types/src/components/ETL/Connection/AgencyBadge.vue.d.ts +8 -0
  153. package/dist/types/src/components/ETL/Connection/ConnectionFiles.vue.d.ts +3 -0
  154. package/dist/types/src/components/ETL/Connection/ConnectionTokens.vue.d.ts +3 -0
  155. package/dist/types/src/components/ETL/Connection/ConnectionVideos.vue.d.ts +3 -0
  156. package/dist/types/src/components/ETL/Connection/StatusDot.vue.d.ts +13 -0
  157. package/dist/types/src/components/ETL/Connection/TokenModal.vue.d.ts +16 -0
  158. package/dist/types/src/components/ETL/Connection.vue.d.ts +3 -0
  159. package/dist/types/src/components/ETL/ConnectionCard.vue.d.ts +16 -0
  160. package/dist/types/src/components/ETL/Connections.vue.d.ts +3 -0
  161. package/dist/types/src/components/ETL/Data.vue.d.ts +3 -0
  162. package/dist/types/src/components/ETL/Layer/LayerIncomingConfig.vue.d.ts +27 -0
  163. package/dist/types/src/components/ETL/Layer/LayerOutgoingConfig.vue.d.ts +27 -0
  164. package/dist/types/src/components/ETL/Layer/utils/QueryInput.vue.d.ts +56 -0
  165. package/dist/types/src/components/ETL/Layer/utils/StatusDot.vue.d.ts +7 -0
  166. package/dist/types/src/components/ETL/Layer.vue.d.ts +3 -0
  167. package/dist/types/src/components/Home.vue.d.ts +13 -0
  168. package/dist/types/src/components/Loading.vue.d.ts +3 -0
  169. package/dist/types/src/components/Login.vue.d.ts +7 -0
  170. package/dist/types/src/components/LostUser.vue.d.ts +3 -0
  171. package/dist/types/src/components/PageFooter.vue.d.ts +3 -0
  172. package/dist/types/src/components/ServerAdmin.vue.d.ts +3 -0
  173. package/dist/types/src/components/VideoWall/Main.vue.d.ts +3 -0
  174. package/dist/types/src/components/VideoWall.vue.d.ts +13 -0
  175. package/dist/types/src/components/util/GroupSelect.vue.d.ts +14 -0
  176. package/dist/types/src/components/util/InitialAuthor.vue.d.ts +6 -0
  177. package/dist/types/src/components/util/StatusDot.vue.d.ts +8 -0
  178. package/dist/types/src/components/util/Upload.vue.d.ts +46 -0
  179. package/dist/types/src/components/util/UploadLogo.vue.d.ts +14 -0
  180. package/dist/types/src/main.d.ts +1 -0
  181. package/dist/types/src/pages/admin/main.d.ts +1 -0
  182. package/dist/types/src/pages/connection/main.d.ts +1 -0
  183. package/dist/types/src/pages/docs/App.vue.d.ts +4 -0
  184. package/dist/types/src/pages/docs/main.d.ts +1 -0
  185. package/dist/types/src/pages/video/main.d.ts +1 -0
  186. package/dist/types/src/std.d.ts +19 -0
  187. package/dist/types/src/stores/brand.d.ts +7 -0
  188. package/dist/types/src/stores/float.d.ts +42 -0
  189. package/dist/types/src/stores/map.d.ts +115 -0
  190. package/dist/types/src/stores/modules/draw.d.ts +35 -0
  191. package/dist/types/src/stores/modules/icons.d.ts +38 -0
  192. package/dist/types/src/stores/modules/menu.d.ts +34 -0
  193. package/dist/types/src/test/setup.d.ts +1 -0
  194. package/dist/types/src/timediff.d.ts +1 -0
  195. package/dist/types/src/types.d.ts +158 -0
  196. package/dist/types/src/video.d.ts +1 -0
  197. package/dist/types/src/workers/atlas-connection.d.ts +12 -0
  198. package/dist/types/src/workers/atlas-database.d.ts +134 -0
  199. package/dist/types/src/workers/atlas-profile.d.ts +39 -0
  200. package/dist/types/src/workers/atlas-team.d.ts +13 -0
  201. package/dist/types/src/workers/atlas.d.ts +19 -0
  202. package/package.json +97 -0
  203. package/src/App.vue +341 -0
  204. package/src/base/chatroom-chats.ts +124 -0
  205. package/src/base/chatroom.ts +202 -0
  206. package/src/base/cot.ts +625 -0
  207. package/src/base/database.ts +150 -0
  208. package/src/base/events.ts +43 -0
  209. package/src/base/filter.ts +163 -0
  210. package/src/base/handler.ts +67 -0
  211. package/src/base/icon.ts +16 -0
  212. package/src/base/notification.ts +163 -0
  213. package/src/base/overlay.ts +558 -0
  214. package/src/base/subscription-feature.ts +224 -0
  215. package/src/base/subscription-log.ts +174 -0
  216. package/src/base/subscription.ts +489 -0
  217. package/src/base/utils/styles.ts +430 -0
  218. package/src/base/validators.ts +66 -0
  219. package/src/components/Admin/AdminConfig.vue +487 -0
  220. package/src/components/Admin/AdminConnections.vue +195 -0
  221. package/src/components/Admin/AdminDatas.vue +165 -0
  222. package/src/components/Admin/AdminExport.vue +121 -0
  223. package/src/components/Admin/AdminImports.vue +222 -0
  224. package/src/components/Admin/AdminLayerTemplate.vue +91 -0
  225. package/src/components/Admin/AdminLayers.vue +301 -0
  226. package/src/components/Admin/AdminMissionTemplate.vue +259 -0
  227. package/src/components/Admin/AdminMissionTemplateLog.vue +273 -0
  228. package/src/components/Admin/AdminMissionTemplates.vue +189 -0
  229. package/src/components/Admin/AdminOverlays.vue +232 -0
  230. package/src/components/Admin/AdminOverlaysEdit.vue +355 -0
  231. package/src/components/Admin/AdminPalette.vue +214 -0
  232. package/src/components/Admin/AdminPaletteFeature.vue +208 -0
  233. package/src/components/Admin/AdminPalettes.vue +189 -0
  234. package/src/components/Admin/AdminServer.vue +143 -0
  235. package/src/components/Admin/AdminTasks.vue +60 -0
  236. package/src/components/Admin/AdminUser.vue +263 -0
  237. package/src/components/Admin/AdminUsers.vue +183 -0
  238. package/src/components/Admin/AdminVideos.vue +60 -0
  239. package/src/components/Admin/Server/ServerConnection.vue +322 -0
  240. package/src/components/Admin/Server/ServerInjectorModal.vue +125 -0
  241. package/src/components/Admin/Server/ServerInjectors.vue +104 -0
  242. package/src/components/Admin/Server/ServerPackages.vue +159 -0
  243. package/src/components/Admin/Server/ServerRepeaters.vue +105 -0
  244. package/src/components/Admin/Server/ServerVideos.vue +83 -0
  245. package/src/components/Admin/Tasks/AdminRawTasks.vue +148 -0
  246. package/src/components/Admin/Tasks/AdminTasks.vue +286 -0
  247. package/src/components/Admin/Videos/AdminVideoLeases.vue +198 -0
  248. package/src/components/Admin/Videos/AdminVideoService.vue +67 -0
  249. package/src/components/Admin/Videos/VideoConfig.vue +132 -0
  250. package/src/components/Admin/Videos/VideoConfigPath.vue +58 -0
  251. package/src/components/Admin/Videos/VideoConfigPort.vue +40 -0
  252. package/src/components/CloudTAK/CoTView.vue +915 -0
  253. package/src/components/CloudTAK/CoordInput.vue +110 -0
  254. package/src/components/CloudTAK/DrawTools.vue +200 -0
  255. package/src/components/CloudTAK/FeatView.vue +242 -0
  256. package/src/components/CloudTAK/GeoJSONInput.vue +226 -0
  257. package/src/components/CloudTAK/MainMenu.vue +190 -0
  258. package/src/components/CloudTAK/MainMenuContents.vue +360 -0
  259. package/src/components/CloudTAK/Map.vue +972 -0
  260. package/src/components/CloudTAK/MapLoading.vue +31 -0
  261. package/src/components/CloudTAK/Menu/Basemaps/EditModal.vue +427 -0
  262. package/src/components/CloudTAK/Menu/Debugger.vue +118 -0
  263. package/src/components/CloudTAK/Menu/Iconset/EditModal.vue +118 -0
  264. package/src/components/CloudTAK/Menu/MenuBasemaps.vue +410 -0
  265. package/src/components/CloudTAK/Menu/MenuChannels.vue +206 -0
  266. package/src/components/CloudTAK/Menu/MenuChat.vue +222 -0
  267. package/src/components/CloudTAK/Menu/MenuChats.vue +167 -0
  268. package/src/components/CloudTAK/Menu/MenuConnections.vue +166 -0
  269. package/src/components/CloudTAK/Menu/MenuContacts.vue +245 -0
  270. package/src/components/CloudTAK/Menu/MenuFeatures.vue +718 -0
  271. package/src/components/CloudTAK/Menu/MenuFeaturesDeleted.vue +217 -0
  272. package/src/components/CloudTAK/Menu/MenuFiles.vue +433 -0
  273. package/src/components/CloudTAK/Menu/MenuIcon.vue +236 -0
  274. package/src/components/CloudTAK/Menu/MenuIconset.vue +128 -0
  275. package/src/components/CloudTAK/Menu/MenuIconsets.vue +266 -0
  276. package/src/components/CloudTAK/Menu/MenuImport.vue +198 -0
  277. package/src/components/CloudTAK/Menu/MenuImports.vue +199 -0
  278. package/src/components/CloudTAK/Menu/MenuItemCard.vue +240 -0
  279. package/src/components/CloudTAK/Menu/MenuMission.vue +324 -0
  280. package/src/components/CloudTAK/Menu/MenuMissions.vue +334 -0
  281. package/src/components/CloudTAK/Menu/MenuOverlayExplorer.vue +434 -0
  282. package/src/components/CloudTAK/Menu/MenuOverlays.vue +560 -0
  283. package/src/components/CloudTAK/Menu/MenuPackage.vue +340 -0
  284. package/src/components/CloudTAK/Menu/MenuPackages.vue +193 -0
  285. package/src/components/CloudTAK/Menu/MenuRoutes.vue +186 -0
  286. package/src/components/CloudTAK/Menu/MenuRoutesNew.vue +193 -0
  287. package/src/components/CloudTAK/Menu/MenuSettings.vue +95 -0
  288. package/src/components/CloudTAK/Menu/MenuSettingsCallsign.vue +12 -0
  289. package/src/components/CloudTAK/Menu/MenuSettingsDisplay.vue +144 -0
  290. package/src/components/CloudTAK/Menu/MenuSettingsTokens.vue +83 -0
  291. package/src/components/CloudTAK/Menu/MenuVideos.vue +436 -0
  292. package/src/components/CloudTAK/Menu/MenuVideosRemote.vue +203 -0
  293. package/src/components/CloudTAK/Menu/Mission/MissionContents.vue +211 -0
  294. package/src/components/CloudTAK/Menu/Mission/MissionCreate.vue +372 -0
  295. package/src/components/CloudTAK/Menu/Mission/MissionInfo.vue +331 -0
  296. package/src/components/CloudTAK/Menu/Mission/MissionInviteModal.vue +107 -0
  297. package/src/components/CloudTAK/Menu/Mission/MissionLayerCreate.vue +77 -0
  298. package/src/components/CloudTAK/Menu/Mission/MissionLayerEdit.vue +77 -0
  299. package/src/components/CloudTAK/Menu/Mission/MissionLayerTree.vue +194 -0
  300. package/src/components/CloudTAK/Menu/Mission/MissionLayers.vue +157 -0
  301. package/src/components/CloudTAK/Menu/Mission/MissionLog.vue +170 -0
  302. package/src/components/CloudTAK/Menu/Mission/MissionLogs.vue +194 -0
  303. package/src/components/CloudTAK/Menu/Mission/MissionTimeline.vue +142 -0
  304. package/src/components/CloudTAK/Menu/Mission/MissionUsers.vue +74 -0
  305. package/src/components/CloudTAK/Menu/Mission/PendingInvites.vue +130 -0
  306. package/src/components/CloudTAK/Menu/Overlays/DeleteModal.vue +57 -0
  307. package/src/components/CloudTAK/Menu/Overlays/TreeCots.vue +497 -0
  308. package/src/components/CloudTAK/Menu/Overlays/TreeMission.vue +32 -0
  309. package/src/components/CloudTAK/Menu/Overlays/TreeVector.vue +113 -0
  310. package/src/components/CloudTAK/Menu/Settings/TokenModal.vue +110 -0
  311. package/src/components/CloudTAK/Menu/Videos/VideoLeaseModal.vue +753 -0
  312. package/src/components/CloudTAK/Menu/Videos/VideosRemoteFeed.vue +75 -0
  313. package/src/components/CloudTAK/Notifications.vue +195 -0
  314. package/src/components/CloudTAK/Query/Magnetic.vue +80 -0
  315. package/src/components/CloudTAK/Query/Reverse.vue +65 -0
  316. package/src/components/CloudTAK/Query/Sun.vue +178 -0
  317. package/src/components/CloudTAK/Query/Weather.vue +195 -0
  318. package/src/components/CloudTAK/QueryView.vue +134 -0
  319. package/src/components/CloudTAK/RadialMenu/LICENSE +21 -0
  320. package/src/components/CloudTAK/RadialMenu/README.md +63 -0
  321. package/src/components/CloudTAK/RadialMenu/RadialMenu.css +85 -0
  322. package/src/components/CloudTAK/RadialMenu/RadialMenu.js +534 -0
  323. package/src/components/CloudTAK/RadialMenu/RadialMenu.vue +257 -0
  324. package/src/components/CloudTAK/RangeInput.vue +155 -0
  325. package/src/components/CloudTAK/RangeRingsInput.vue +269 -0
  326. package/src/components/CloudTAK/util/Breadcrumb.vue +85 -0
  327. package/src/components/CloudTAK/util/ChannelInfo.vue +25 -0
  328. package/src/components/CloudTAK/util/Contact.vue +131 -0
  329. package/src/components/CloudTAK/util/ContactPuck.vue +40 -0
  330. package/src/components/CloudTAK/util/Coordinate.vue +313 -0
  331. package/src/components/CloudTAK/util/CoordinateType.vue +134 -0
  332. package/src/components/CloudTAK/util/CopyButton.vue +67 -0
  333. package/src/components/CloudTAK/util/CopyField.vue +286 -0
  334. package/src/components/CloudTAK/util/DrawOverlay.vue +254 -0
  335. package/src/components/CloudTAK/util/EmptyInfo.vue +71 -0
  336. package/src/components/CloudTAK/util/FeatureIcon.vue +134 -0
  337. package/src/components/CloudTAK/util/FeatureRow.vue +206 -0
  338. package/src/components/CloudTAK/util/FloatingAttachment.vue +191 -0
  339. package/src/components/CloudTAK/util/FloatingVideo.vue +654 -0
  340. package/src/components/CloudTAK/util/GenericSelect.vue +125 -0
  341. package/src/components/CloudTAK/util/GroupSelect.vue +129 -0
  342. package/src/components/CloudTAK/util/Icons.vue +146 -0
  343. package/src/components/CloudTAK/util/JSONModal.vue +57 -0
  344. package/src/components/CloudTAK/util/Keywords.vue +31 -0
  345. package/src/components/CloudTAK/util/LineLength.vue +121 -0
  346. package/src/components/CloudTAK/util/MenuItem.vue +21 -0
  347. package/src/components/CloudTAK/util/MenuTemplate.vue +137 -0
  348. package/src/components/CloudTAK/util/MultipleSelect.vue +103 -0
  349. package/src/components/CloudTAK/util/NotificationIcon.vue +50 -0
  350. package/src/components/CloudTAK/util/NotificationToast.vue +125 -0
  351. package/src/components/CloudTAK/util/PolygonArea.vue +109 -0
  352. package/src/components/CloudTAK/util/PropertyAttachments.vue +224 -0
  353. package/src/components/CloudTAK/util/PropertyBattery.vue +44 -0
  354. package/src/components/CloudTAK/util/PropertyBearing.vue +148 -0
  355. package/src/components/CloudTAK/util/PropertyCreator.vue +61 -0
  356. package/src/components/CloudTAK/util/PropertyDistance.vue +182 -0
  357. package/src/components/CloudTAK/util/PropertyElevation.vue +84 -0
  358. package/src/components/CloudTAK/util/PropertyEmail.vue +44 -0
  359. package/src/components/CloudTAK/util/PropertyLinks.vue +148 -0
  360. package/src/components/CloudTAK/util/PropertyMetadata.vue +53 -0
  361. package/src/components/CloudTAK/util/PropertyMilSym.vue +62 -0
  362. package/src/components/CloudTAK/util/PropertyPhone.vue +61 -0
  363. package/src/components/CloudTAK/util/PropertySensor.vue +129 -0
  364. package/src/components/CloudTAK/util/PropertySpeed.vue +91 -0
  365. package/src/components/CloudTAK/util/PropertyStyle.vue +202 -0
  366. package/src/components/CloudTAK/util/PropertyTimes.vue +95 -0
  367. package/src/components/CloudTAK/util/PropertyType.vue +274 -0
  368. package/src/components/CloudTAK/util/SearchBox.vue +314 -0
  369. package/src/components/CloudTAK/util/SelectFeats.vue +190 -0
  370. package/src/components/CloudTAK/util/SettingsCallsign.vue +151 -0
  371. package/src/components/CloudTAK/util/Share.vue +496 -0
  372. package/src/components/CloudTAK/util/ShareToMission.vue +223 -0
  373. package/src/components/CloudTAK/util/ShareToPackage.vue +250 -0
  374. package/src/components/CloudTAK/util/SlideDownHeader.vue +92 -0
  375. package/src/components/CloudTAK/util/StandardItem.vue +42 -0
  376. package/src/components/CloudTAK/util/Subscriptions.vue +151 -0
  377. package/src/components/CloudTAK/util/TagEntry.vue +301 -0
  378. package/src/components/CloudTAK/util/UploadImport.vue +195 -0
  379. package/src/components/CloudTAK/util/VideoLeaseSourceType.vue +66 -0
  380. package/src/components/CloudTAK/util/WarnChannels.vue +56 -0
  381. package/src/components/CloudTAK/util/WarnConfiguration.vue +34 -0
  382. package/src/components/Configure.vue +259 -0
  383. package/src/components/ETL/Connection/AgencyBadge.vue +102 -0
  384. package/src/components/ETL/Connection/AgencySelect.vue +185 -0
  385. package/src/components/ETL/Connection/CertificateLogin.vue +75 -0
  386. package/src/components/ETL/Connection/CertificateMachineUser.vue +228 -0
  387. package/src/components/ETL/Connection/CertificateP12.vue +104 -0
  388. package/src/components/ETL/Connection/CertificateRaw.vue +50 -0
  389. package/src/components/ETL/Connection/ConnectionData.vue +174 -0
  390. package/src/components/ETL/Connection/ConnectionFiles.vue +182 -0
  391. package/src/components/ETL/Connection/ConnectionGroups.vue +181 -0
  392. package/src/components/ETL/Connection/ConnectionLayer.vue +180 -0
  393. package/src/components/ETL/Connection/ConnectionTokens.vue +147 -0
  394. package/src/components/ETL/Connection/ConnectionVideos.vue +151 -0
  395. package/src/components/ETL/Connection/Events.vue +88 -0
  396. package/src/components/ETL/Connection/StatusDot.vue +48 -0
  397. package/src/components/ETL/Connection/TokenModal.vue +136 -0
  398. package/src/components/ETL/Connection.vue +194 -0
  399. package/src/components/ETL/ConnectionCard.vue +265 -0
  400. package/src/components/ETL/ConnectionEdit.vue +448 -0
  401. package/src/components/ETL/Connections.vue +148 -0
  402. package/src/components/ETL/Data/DataFiles.vue +245 -0
  403. package/src/components/ETL/Data/DataGroups.vue +45 -0
  404. package/src/components/ETL/Data/DataLayer.vue +144 -0
  405. package/src/components/ETL/Data/Location.vue +238 -0
  406. package/src/components/ETL/Data.vue +259 -0
  407. package/src/components/ETL/DataEdit.vue +212 -0
  408. package/src/components/ETL/Layer/LayerAlarm.vue +307 -0
  409. package/src/components/ETL/Layer/LayerDeployment.vue +388 -0
  410. package/src/components/ETL/Layer/LayerEnvironment.vue +259 -0
  411. package/src/components/ETL/Layer/LayerIncomingConfig.vue +401 -0
  412. package/src/components/ETL/Layer/LayerIncomingEnvironmentArcGIS.vue +354 -0
  413. package/src/components/ETL/Layer/LayerIncomingSchema.vue +138 -0
  414. package/src/components/ETL/Layer/LayerIncomingStyles.vue +349 -0
  415. package/src/components/ETL/Layer/LayerOutgoingConfig.vue +204 -0
  416. package/src/components/ETL/Layer/LayerOutgoingEnvironmentArcGIS.vue +279 -0
  417. package/src/components/ETL/Layer/utils/QueryInput.vue +52 -0
  418. package/src/components/ETL/Layer/utils/Schema.vue +322 -0
  419. package/src/components/ETL/Layer/utils/SchemaModal.vue +77 -0
  420. package/src/components/ETL/Layer/utils/StatusDot.vue +38 -0
  421. package/src/components/ETL/Layer/utils/StyleLinkModal.vue +83 -0
  422. package/src/components/ETL/Layer/utils/StyleLinks.vue +139 -0
  423. package/src/components/ETL/Layer/utils/StyleSingle.vue +885 -0
  424. package/src/components/ETL/Layer/utils/StyleTemplate.vue +97 -0
  425. package/src/components/ETL/Layer/utils/TaskModal.vue +217 -0
  426. package/src/components/ETL/Layer.vue +560 -0
  427. package/src/components/ETL/LayerEdit.vue +276 -0
  428. package/src/components/ETL/Styling/Layer.vue +311 -0
  429. package/src/components/ETL/Styling/ObjectInput.vue +46 -0
  430. package/src/components/ETL/Styling/Style.vue +233 -0
  431. package/src/components/Home.vue +17 -0
  432. package/src/components/Loading.vue +42 -0
  433. package/src/components/Login.vue +198 -0
  434. package/src/components/LostUser.vue +31 -0
  435. package/src/components/PageFooter.vue +41 -0
  436. package/src/components/ServerAdmin.vue +408 -0
  437. package/src/components/VideoWall/Main.vue +79 -0
  438. package/src/components/VideoWall.vue +17 -0
  439. package/src/components/util/DataSelect.vue +148 -0
  440. package/src/components/util/EsriFilter.vue +140 -0
  441. package/src/components/util/EsriPortal.vue +422 -0
  442. package/src/components/util/EsriPortalCreate.vue +72 -0
  443. package/src/components/util/EsriServer.vue +368 -0
  444. package/src/components/util/GroupSelect.vue +166 -0
  445. package/src/components/util/IconSelect.vue +302 -0
  446. package/src/components/util/InitialAuthor.vue +28 -0
  447. package/src/components/util/LayerSelect.vue +162 -0
  448. package/src/components/util/LayerTaskSelect.vue +311 -0
  449. package/src/components/util/LayerTemplateSelect.vue +173 -0
  450. package/src/components/util/LoginModal.vue +110 -0
  451. package/src/components/util/PublicTilesSelect.vue +171 -0
  452. package/src/components/util/StatusDot.vue +31 -0
  453. package/src/components/util/TableFooter.vue +64 -0
  454. package/src/components/util/TableHeader.vue +127 -0
  455. package/src/components/util/Upload.vue +220 -0
  456. package/src/components/util/UploadCSV.vue +46 -0
  457. package/src/components/util/UploadLogo.vue +83 -0
  458. package/src/components/util/UploadP12.vue +93 -0
  459. package/src/components/util/UserSelect.vue +151 -0
  460. package/src/derived-types.d.ts +35243 -0
  461. package/src/main.ts +264 -0
  462. package/src/pages/admin/main.ts +208 -0
  463. package/src/pages/connection/main.ts +158 -0
  464. package/src/pages/docs/App.vue +19 -0
  465. package/src/pages/docs/main.ts +6 -0
  466. package/src/pages/video/main.ts +27 -0
  467. package/src/std.ts +152 -0
  468. package/src/stores/brand.ts +28 -0
  469. package/src/stores/float.ts +113 -0
  470. package/src/stores/map.ts +931 -0
  471. package/src/stores/modules/draw.ts +413 -0
  472. package/src/stores/modules/icons.ts +185 -0
  473. package/src/stores/modules/menu.ts +281 -0
  474. package/src/test/setup.ts +43 -0
  475. package/src/timediff.ts +33 -0
  476. package/src/types.ts +226 -0
  477. package/src/video.ts +27 -0
  478. package/src/vite-env.d.ts +9 -0
  479. package/src/workers/atlas-connection.ts +236 -0
  480. package/src/workers/atlas-database.ts +764 -0
  481. package/src/workers/atlas-profile.ts +374 -0
  482. package/src/workers/atlas-team.ts +90 -0
  483. package/src/workers/atlas.ts +81 -0
@@ -0,0 +1,972 @@
1
+ <template>
2
+ <div
3
+ class='d-flex position-relative'
4
+ style='height: calc(100vh) !important;'
5
+ data-bs-theme='dark'
6
+ data-bs-theme-base='neutral'
7
+ data-bs-theme-primary='blue'
8
+ >
9
+ <div
10
+ ref='map'
11
+ style='width: 100%;'
12
+ />
13
+
14
+ <MapLoading v-if='loading || !mapStore.isLoaded' />
15
+
16
+ <template v-if='mapStore.isLoaded && !loading'>
17
+ <WarnConfiguration
18
+ v-if='warnConfiguration'
19
+ @close='warnConfiguration = false'
20
+ />
21
+ <WarnChannels
22
+ v-else-if='warnChannels'
23
+ @close='warnChannels = false'
24
+ />
25
+
26
+ <DrawOverlay
27
+ v-if='mapStore.draw.mode !== DrawToolMode.STATIC'
28
+ />
29
+
30
+ <GeoJSONInput
31
+ v-if='mapStore.toImport.length'
32
+ :features='mapStore.toImport'
33
+ @close='mapStore.toImport = []'
34
+ @done='mapStore.toImport = []'
35
+ />
36
+
37
+ <div
38
+ v-if='mode === "SetLocation"'
39
+ class='position-absolute bottom-0 text-white bg-dark rounded-top'
40
+ style='
41
+ z-index: 1;
42
+ left: calc(50% - 250px);
43
+ width: 500px;
44
+ '
45
+ >
46
+ <div
47
+ class='card user-select-none'
48
+ >
49
+ <div class='card-header'>
50
+ <div class='col-8'>
51
+ <IconLocationPin
52
+ class='me-2'
53
+ :size='20'
54
+ />
55
+ <span>Click on the map to {{ mapStore.location === LocationState.Preset ? 'update' : 'set' }} your location</span>
56
+ </div>
57
+ <div class='col-4 d-flex align-items-center'>
58
+ <div class='ms-auto btn-list'>
59
+ <button
60
+ class='btn btn-sm btn-outline-light'
61
+ @click='exitManualMode'
62
+ >
63
+ <IconLocation
64
+ :size='16'
65
+ class='me-1'
66
+ />
67
+ Use GPS
68
+ </button>
69
+ <TablerIconButton
70
+ title='Cancel Manual Location'
71
+ @click='cancelLocationSetting'
72
+ >
73
+ <IconX
74
+ :size='24'
75
+ stroke='1'
76
+ />
77
+ </TablerIconButton>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ <div
84
+ class='position-absolute bottom-0 begin-0 text-white'
85
+ style='
86
+ z-index: 1;
87
+ width: 250px;
88
+ height: 40px;
89
+ background-color: rgba(0, 0, 0, 0.5);
90
+ '
91
+ :style='{
92
+ "border-radius": mapStore.selected.size ? "0px" : "0px 6px 0px 0px"
93
+ }'
94
+ >
95
+ <div
96
+ class='d-flex align-items-center'
97
+ style='height: 40px'
98
+ >
99
+ <div
100
+ class='hover-button h-100 d-flex align-items-center'
101
+ style='width: 40px;'
102
+ >
103
+ <TablerIconButton
104
+ v-if='mapStore.location === LocationState.Live'
105
+ :title='locationTooltip'
106
+ :hover='false'
107
+ @click='setLocation'
108
+ >
109
+ <IconLocation
110
+ style='margin: 5px 8px'
111
+ :size='20'
112
+ stroke='1'
113
+ :color='locationColor'
114
+ />
115
+ </TablerIconButton>
116
+ <TablerIconButton
117
+ v-else-if='mapStore.location === LocationState.Preset'
118
+ :title='locationTooltip'
119
+ :hover='false'
120
+ @click='setLocation'
121
+ >
122
+ <IconLocationPin
123
+ title='Manual Location - Click to enable GPS'
124
+ style='margin: 5px 8px'
125
+ :size='20'
126
+ stroke='1'
127
+ />
128
+ </TablerIconButton>
129
+ <TablerIconButton
130
+ v-else
131
+ title='Set Your Location Button'
132
+ :hover='false'
133
+ @click='setLocation'
134
+ >
135
+ <IconLocationOff
136
+ title='Set Your Location Button (No Location currently set)'
137
+ style='margin: 5px 8px'
138
+ :size='20'
139
+ stroke='1'
140
+ />
141
+ </TablerIconButton>
142
+ </div>
143
+ <div
144
+ v-tooltip='"Zoom To Location"'
145
+ style='line-height: 40px; width: calc(100% - 40px);'
146
+ class='h-100 cursor-pointer text-center px-2 text-truncate subheader text-white hover-button user-select-none'
147
+ @click='toLocation'
148
+ v-text='mapStore.callsign'
149
+ />
150
+ </div>
151
+ </div>
152
+ <div
153
+ v-if='mapStore.selected.size'
154
+ class='position-absolute begin-0 text-white bg-dark'
155
+ style='
156
+ bottom: 40px;
157
+ width: 250px;
158
+ '
159
+ >
160
+ <SelectFeats :selected='mapStore.selected' />
161
+ </div>
162
+
163
+ <div
164
+ v-if='mode === "Default"'
165
+ class='position-absolute top-0 beginning-0 text-white'
166
+ >
167
+ <div
168
+ style='
169
+ z-index: 1;
170
+ height: 40px;
171
+ max-width: 400px;
172
+ background-color: rgba(0, 0, 0, 0.5);
173
+ border-radius: 0px 0px 6px 0px;
174
+ '
175
+ >
176
+ <template v-if='!mapStore.mission'>
177
+ <div
178
+ class='hover-button d-flex align-items-center user-select-none cursor-pointer'
179
+ @click='router.push("/menu/missions")'
180
+ >
181
+ <IconMap
182
+ :size='32'
183
+ stroke='1'
184
+ style='margin: 3px 3px'
185
+ />
186
+ <div class='me-3'>
187
+ No Mission
188
+ </div>
189
+ </div>
190
+ </template>
191
+ <template v-else>
192
+ <div class='d-flex align-items-center user-select-none'>
193
+ <IconAmbulance
194
+ :size='32'
195
+ stroke='1'
196
+ style='margin: 3px 3px'
197
+ />
198
+
199
+ <a
200
+ class='me-3 text-truncate cursor-pointer text-white'
201
+ @click='router.push(`/menu/missions/${mapStore.mission.meta.guid}`)'
202
+ v-text='mapStore.mission.meta.name'
203
+ />
204
+ </div>
205
+ </template>
206
+ </div>
207
+ <div
208
+ class='border'
209
+ style='
210
+ z-index: 1;
211
+ width: 40px;
212
+ background-color: rgba(0, 0, 0, 0.2);
213
+ border-radius: 0px 0px 6px 0px;
214
+ '
215
+ >
216
+ <div>
217
+ <IconSearch
218
+ v-tooltip='"Search"'
219
+ tabindex='0'
220
+ title='Search Button'
221
+ :size='32'
222
+ stroke='2'
223
+ :color='searchBoxShown ? "#1E90FF" : "#ffffff"'
224
+ style='margin: 3px 2px'
225
+ class='cursor-pointer hover-button'
226
+ @click='searchBoxShown = !searchBoxShown'
227
+ />
228
+ </div>
229
+
230
+ <div
231
+ v-if='mapStore.bearing !== 0'
232
+ style='margin: 3px 3px'
233
+ class='cursor-pointer hover-button'
234
+ @click='mapStore.map.setBearing(0)'
235
+ >
236
+ <IconCircleArrowUp
237
+ v-tooltip='"Snap to North"'
238
+ tabindex='0'
239
+ :alt='`Map Rotated to ${humanBearing}`'
240
+ :transform='`rotate(${360 - mapStore.bearing})`'
241
+ :size='32'
242
+ stroke='2'
243
+ />
244
+ <div
245
+ v-if='mapStore.bearing !== 0'
246
+ class='text-center'
247
+ v-text='humanBearing'
248
+ />
249
+ </div>
250
+ <div
251
+ v-if='mapStore.pitch !== 0'
252
+ style='margin: 3px 3px'
253
+ class='cursor-pointer hover-button'
254
+ @click='mapStore.map.setPitch(0)'
255
+ >
256
+ <IconAngle
257
+ v-tooltip='"Snap Flat"'
258
+ tabindex='0'
259
+ :alt='`Map Pitch to ${humanPitch}`'
260
+ :size='32'
261
+ stroke='2'
262
+ />
263
+ <div
264
+ v-if='mapStore.pitch !== 0'
265
+ class='text-center'
266
+ v-text='humanPitch'
267
+ />
268
+ </div>
269
+ <div
270
+ v-if='displayZoom'
271
+ >
272
+ <IconPlus
273
+ v-tooltip='"Zoom In"'
274
+ role='button'
275
+ tabindex='0'
276
+ title='Zoom In Button'
277
+ :size='32'
278
+ stroke='2'
279
+ class='cursor-pointer hover-button'
280
+ style='margin: 3px 3px'
281
+ @click='mapStore.map.setZoom(mapStore.map.getZoom() + 1);'
282
+ />
283
+ <IconMinus
284
+ v-tooltip='"Zoom Out"'
285
+ role='button'
286
+ tabindex='0'
287
+ title='Zoom Out Button'
288
+ :size='32'
289
+ stroke='2'
290
+ class='cursor-pointer hover-button'
291
+ style='margin: 3px 3px'
292
+ @click='mapStore.map.setZoom(mapStore.map.getZoom() - 1);'
293
+ />
294
+ </div>
295
+
296
+ <IconMountain
297
+ v-if='mapStore.hasTerrain'
298
+ v-tooltip='mapStore.isTerrainEnabled ? "Disable 3D Terrain" : "Enable 3D Terrain"'
299
+ role='button'
300
+ tabindex='0'
301
+ title='3D Terrain'
302
+ :size='32'
303
+ stroke='2'
304
+ class='cursor-pointer hover-button'
305
+ :color='mapStore.isTerrainEnabled ? "#1E90FF" : "#FFFFFF"'
306
+ style='margin: 3px 3px'
307
+ @click='mapStore.isTerrainEnabled ? mapStore.removeTerrain() : mapStore.addTerrain()'
308
+ />
309
+
310
+ <IconLockAccess
311
+ v-if='
312
+ (mapStore.radial.cot && mapStore.locked.length >= 2)
313
+ || (!mapStore.radial.cot && mapStore.locked.length >= 1)
314
+ '
315
+ v-tooltip='"Map is locked to marker - Click to Unlock"'
316
+ title='Map is locked to marker - Click to Unlock'
317
+ class='cursor-pointer hover-button'
318
+ role='button'
319
+ tabindex='0'
320
+ color='red'
321
+ :size='32'
322
+ stroke='2'
323
+ style='margin: 3px 3px'
324
+ @click='mapStore.locked.splice(0, mapStore.locked.length)'
325
+ />
326
+ </div>
327
+ </div>
328
+
329
+ <TablerModal
330
+ v-if='searchBoxShown'
331
+ size='lg'
332
+ >
333
+ <div class='modal-header'>
334
+ <div class='modal-title'>
335
+ Search
336
+ </div>
337
+ <button
338
+ type='button'
339
+ class='btn-close'
340
+ aria-label='Close'
341
+ @click='searchBoxShown = false'
342
+ />
343
+ </div>
344
+ <div class='modal-body'>
345
+ <SearchBox
346
+ :autofocus='true'
347
+ @select='searchBoxShown = false'
348
+ />
349
+ </div>
350
+ </TablerModal>
351
+
352
+ <div
353
+ v-if='mapStore.isLoaded && mode === "Default"'
354
+ class='d-flex position-absolute top-0 text-white py-2'
355
+ style='
356
+ z-index: 2;
357
+ width: 120px;
358
+ right: 60px;
359
+ padding-left: 10px;
360
+ background-color: rgba(0, 0, 0, 0.5);
361
+ border-radius: 0px 0px 0px 6px;
362
+ '
363
+ >
364
+ <TablerDropdown>
365
+ <TablerIconButton
366
+ id='map-notifications'
367
+ title='Notifications Icon'
368
+ class='hover-button'
369
+ :hover='false'
370
+ >
371
+ <IconBell
372
+ :size='40'
373
+ stroke='1'
374
+ />
375
+ </TablerIconButton>
376
+ <template #dropdown>
377
+ <Notifications />
378
+ </template>
379
+ </TablerDropdown>
380
+
381
+ <span
382
+ v-if='notifications'
383
+ class='badge bg-red mb-2'
384
+ />
385
+ <span
386
+ v-else
387
+ style='width: 10px;'
388
+ />
389
+
390
+ <DrawTools />
391
+ </div>
392
+
393
+ <div
394
+ v-if='mode === "Default"'
395
+ class='position-absolute top-0 end-0 text-white py-2'
396
+ style='z-index: 1; width: 60px; background-color: rgba(0, 0, 0, 0.5);'
397
+ >
398
+ <TablerIconButton
399
+ v-if='noMenuShown'
400
+ title='Open Menu'
401
+ class='mx-2 hover-button'
402
+ :hover='false'
403
+ @click='router.push("/menu")'
404
+ >
405
+ <IconMenu2
406
+ :size='40'
407
+ stroke='1'
408
+ />
409
+ </TablerIconButton>
410
+ <TablerIconButton
411
+ v-else
412
+ title='Close Menu'
413
+ class='mx-2 cursor-pointer'
414
+ @click='closeAllMenu'
415
+ >
416
+ <IconX
417
+ :size='40'
418
+ stroke='1'
419
+ />
420
+ </TablerIconButton>
421
+ </div>
422
+
423
+
424
+ <MainMenu
425
+ v-if='
426
+ mapStore.isLoaded
427
+ && (
428
+ (noMenuShown && !isMobileDetected)
429
+ || (!noMenuShown)
430
+ )
431
+ '
432
+ :compact='noMenuShown'
433
+ />
434
+
435
+ <MultipleSelect
436
+ v-if='mapStore.select.feats.length'
437
+ @selected='selectFeat($event)'
438
+ />
439
+
440
+ <RadialMenu
441
+ v-else-if='mapStore.radial.mode'
442
+ @close='closeRadial'
443
+ @click='handleRadial($event)'
444
+ />
445
+
446
+ <CloudTAKFeatView
447
+ v-if='feat && mode === "Default" && route.name === "home"'
448
+ :key='feat.id'
449
+ :feat='feat'
450
+ />
451
+
452
+ <template
453
+ v-for='float in floatStore.panes.values()'
454
+ :key='float.uid'
455
+ >
456
+ <FloatingVideo
457
+ v-if='float.type === PaneType.VIDEO'
458
+ :title='float.name || "Video Stream"'
459
+ :uid='float.uid'
460
+ @close='floatStore.panes.delete(float.uid)'
461
+ />
462
+ <FloatingAttachment
463
+ v-if='float.type === PaneType.ATTACHMENT'
464
+ :uid='float.uid'
465
+ @close='floatStore.panes.delete(float.uid)'
466
+ />
467
+ </template>
468
+
469
+ <template v-if='upload.shown'>
470
+ <TablerModal>
471
+ <div class='modal-status bg-red' />
472
+ <button
473
+ type='button'
474
+ class='btn-close'
475
+ aria-label='Close'
476
+ @click='upload.shown = false'
477
+ />
478
+ <div class='modal-body text-white'>
479
+ <UploadImport
480
+ :dragging='upload.dragging'
481
+ :cancel-button='false'
482
+ @close='upload.shown = false'
483
+ @done='fileUpload($event)'
484
+ />
485
+ </div>
486
+ </TablerModal>
487
+ </template>
488
+ </template>
489
+ </div>
490
+ </template>
491
+
492
+ <script setup lang='ts'>
493
+ import GeoJSONInput from './GeoJSONInput.vue';
494
+ import { ref, watch, computed, toRaw, onMounted, onBeforeUnmount, useTemplateRef } from 'vue';
495
+ import {useRoute, useRouter } from 'vue-router';
496
+ import FloatingVideo from './util/FloatingVideo.vue';
497
+ import FloatingAttachment from './util/FloatingAttachment.vue';
498
+ import DrawOverlay from './util/DrawOverlay.vue';
499
+ import WarnChannels from './util/WarnChannels.vue';
500
+ import Notifications from './Notifications.vue';
501
+ import SearchBox from './util/SearchBox.vue';
502
+ import WarnConfiguration from './util/WarnConfiguration.vue';
503
+ import DrawTools from './DrawTools.vue';
504
+ import type { MapGeoJSONFeature, LngLatLike, MapMouseEvent } from 'maplibre-gl';
505
+ import type { Feature } from '../../types.ts';
506
+ import CloudTAKFeatView from './FeatView.vue';
507
+ import {
508
+ IconSearch,
509
+ IconLocationOff,
510
+ IconLocationPin,
511
+ IconLocation,
512
+ IconMenu2,
513
+ IconPlus,
514
+ IconMinus,
515
+ IconLockAccess,
516
+ IconAmbulance,
517
+ IconMap,
518
+ IconX,
519
+ IconBell,
520
+ IconAngle,
521
+ IconCircleArrowUp,
522
+ IconMountain,
523
+ } from '@tabler/icons-vue';
524
+ import SelectFeats from './util/SelectFeats.vue';
525
+ import MultipleSelect from './util/MultipleSelect.vue';
526
+ import MainMenu from './MainMenu.vue';
527
+ import { from } from 'rxjs';
528
+ import { useObservable } from '@vueuse/rxjs';
529
+ import {
530
+ TablerIconButton,
531
+ TablerDropdown,
532
+ TablerModal,
533
+ } from '@tak-ps/vue-tabler';
534
+ import { LocationState } from '../../base/events.ts';
535
+ import TAKNotification from '../../base/notification.ts';
536
+ import COT from '../../base/cot.ts';
537
+ import MapLoading from './MapLoading.vue';
538
+ import 'maplibre-gl/dist/maplibre-gl.css';
539
+ import RadialMenu from './RadialMenu/RadialMenu.vue';
540
+ import { useMapStore } from '../../stores/map.ts';
541
+ import { DrawToolMode } from '../../stores/modules/draw.ts';
542
+ import { useFloatStore, PaneType } from '../../stores/float.ts';
543
+ import { liveQuery } from 'dexie';
544
+ import UploadImport from './util/UploadImport.vue'
545
+ const mapStore = useMapStore();
546
+ const floatStore = useFloatStore();
547
+ const router = useRouter();
548
+ const route = useRoute();
549
+
550
+ const emit = defineEmits(['err']);
551
+
552
+ const mode = ref<string>('Default');
553
+ const locationClickHandler = ref<((e: MapMouseEvent) => void) | null>(null);
554
+ const height = ref<number>(window.innerHeight);
555
+ const width = ref<number>(window.innerWidth);
556
+
557
+ mapStore.isMobileDetected = detectMobile();
558
+
559
+ // Show a popup if no channels are selected on load
560
+ const warnChannels = ref<boolean>(false)
561
+
562
+ // Show a popup if role/groups hasn't been set
563
+ const warnConfiguration = ref<boolean>(false);
564
+
565
+ const searchBoxShown = ref(false);
566
+ const feat = ref() // Show the Feat Viewer sidebar
567
+
568
+ const upload = ref({
569
+ shown: false,
570
+ dragging: false
571
+ })
572
+
573
+ // Interval for pushing GeoJSON Map Updates (CoT)
574
+ const timer = ref<ReturnType<typeof setInterval> | undefined>()
575
+
576
+ const loading = ref(true)
577
+
578
+ const notifications = useObservable<number>(
579
+ from(liveQuery(async () => {
580
+ return await TAKNotification.count()
581
+ }))
582
+ );
583
+
584
+ function detectMobile() {
585
+ //TODO: This needs to follow something like:
586
+ // https://stackoverflow.com/questions/47219272/how-can-i-monitor-changing-window-sizes-in-vue
587
+ return (
588
+ ( width.value <= 576 )
589
+ || ( height.value <= 576 )
590
+ );
591
+ }
592
+
593
+ const isMobileDetected = computed(() => {
594
+ return detectMobile();
595
+ });
596
+
597
+ watch(isMobileDetected, () => {
598
+ mapStore.isMobileDetected = isMobileDetected.value;
599
+ });
600
+
601
+ const displayZoom = computed(() => {
602
+ if (mapStore.zoom === 'conditional') {
603
+ return isMobileDetected;
604
+ } else {
605
+ return mapStore.zoom === 'always' ? true : false;
606
+ }
607
+ })
608
+
609
+
610
+ const humanBearing = computed(() => {
611
+ if (mapStore.bearing < 0) {
612
+ return Math.round(mapStore.bearing * -1) + '°'
613
+ } else {
614
+ return Math.round(360 - mapStore.bearing) + '°';
615
+ }
616
+ })
617
+
618
+ const humanPitch = computed(() => {
619
+ return Math.round(mapStore.pitch) + '°'
620
+ })
621
+
622
+ // Reactive location accuracy
623
+ const locationAccuracy = ref<number | undefined>(undefined);
624
+
625
+ // Watch for location updates and get accuracy
626
+ watch(() => mapStore.location, async () => {
627
+ if (mapStore.location === LocationState.Live && !mapStore.manualLocationMode) {
628
+ try {
629
+ const location = await mapStore.worker.profile.location;
630
+ locationAccuracy.value = location.accuracy;
631
+ } catch {
632
+ locationAccuracy.value = undefined;
633
+ }
634
+ } else {
635
+ locationAccuracy.value = undefined;
636
+ }
637
+ }, { immediate: true });
638
+
639
+ const locationColor = computed(() => {
640
+ if (mapStore.location !== LocationState.Live || !locationAccuracy.value) return '#ffffff';
641
+
642
+ const accuracy = locationAccuracy.value;
643
+ // Color-code based on accuracy ranges
644
+ if (accuracy <= 50) return '#22c55e'; // Green - high accuracy
645
+ if (accuracy <= 200) return '#eab308'; // Yellow - medium accuracy
646
+ return '#ef4444'; // Red - low accuracy
647
+ })
648
+
649
+ const locationTooltip = computed(() => {
650
+ if (mode.value === 'SetLocation') {
651
+ return 'Click on map to set location';
652
+ }
653
+
654
+ if (mapStore.location === LocationState.Preset) {
655
+ return 'Manual Location - Click to adjust or switch to GPS';
656
+ }
657
+
658
+ if (mapStore.location === LocationState.Live && locationAccuracy.value) {
659
+ const accuracy = locationAccuracy.value;
660
+ // Convert to user's preferred distance unit
661
+ if (mapStore.distanceUnit === 'mile') {
662
+ const accuracyFt = Math.round(accuracy * 3.28084);
663
+ return `Live Location (±${accuracyFt}ft) - Click to set manually`;
664
+ } else {
665
+ return `Live Location (±${Math.round(accuracy)}m) - Click to set manually`;
666
+ }
667
+ }
668
+
669
+ return 'Set Your Location - Click to enable GPS or set manually';
670
+ })
671
+
672
+ const mapRef = useTemplateRef<HTMLElement>('map');
673
+
674
+ const noMenuShown = computed<boolean>(() => {
675
+ return !feat.value
676
+ && (!route.name || !String(route.name).startsWith('home-menu'))
677
+ });
678
+
679
+
680
+
681
+ onMounted(async () => {
682
+ // ensure uncaught errors in the stack are captured into vue context
683
+ window.addEventListener('error', (evt) => {
684
+ console.error(evt);
685
+ evt.preventDefault();
686
+ emit('err', new Error(evt.message));
687
+ });
688
+
689
+ window.addEventListener('resize', () => {
690
+ height.value = window.innerHeight;
691
+ width.value = window.innerWidth;
692
+ });
693
+
694
+ await mountMap();
695
+
696
+ // TODO these are no longer reactive, does it matter?
697
+ warnChannels.value = await mapStore.worker.profile.hasNoChannels();
698
+ warnConfiguration.value = await mapStore.worker.profile.hasNoConfiguration();
699
+
700
+ loading.value = false;
701
+
702
+ window.addEventListener('dragover', (e) => {
703
+ e.preventDefault();
704
+
705
+ const dt = e.dataTransfer;
706
+ if (dt && dt.types && (dt.types.indexOf ? dt.types.indexOf('Files') != -1 : dt.types.includes('Files'))) {
707
+ upload.value.shown = true;
708
+ upload.value.dragging = true;
709
+ }
710
+ });
711
+
712
+ window.addEventListener('keydown', (e) => {
713
+ if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
714
+ e.preventDefault();
715
+ searchBoxShown.value = true;
716
+ }
717
+ });
718
+
719
+ window.addEventListener('keyup', (e) => {
720
+ if (e.key == 'Escape') {
721
+ if (searchBoxShown.value) {
722
+ searchBoxShown.value = false;
723
+ } else if (mapStore.radial.mode) {
724
+ closeRadial()
725
+ } else if (mapStore.select.feats) {
726
+ mapStore.select.feats = [];
727
+ } else if (route.path.startsWith("/")) {
728
+ router.push("/");
729
+ }
730
+ }
731
+ });
732
+ });
733
+
734
+ onBeforeUnmount(() => {
735
+ if (timer.value) {
736
+ window.clearInterval(timer.value);
737
+ }
738
+
739
+ // Clean up GPS watch
740
+ if (mapStore.gpsWatchId !== null) {
741
+ navigator.geolocation.clearWatch(mapStore.gpsWatchId);
742
+ }
743
+
744
+ mapStore.destroy();
745
+ });
746
+
747
+ function selectFeat(selectedFeat: MapGeoJSONFeature | COT) {
748
+ mapStore.select.feats = [];
749
+
750
+ if (selectedFeat instanceof COT) {
751
+ router.push(`/cot/${selectedFeat.properties.id}`);
752
+ } else {
753
+ router.push(`/`);
754
+ feat.value = selectedFeat;
755
+ }
756
+ }
757
+
758
+ function closeAllMenu() {
759
+ feat.value = false;
760
+ router.push("/");
761
+ }
762
+
763
+ function closeRadial() {
764
+ mapStore.radial.mode = undefined;
765
+ mapStore.radial.cot = undefined;
766
+ }
767
+
768
+ async function toLocation() {
769
+ const location = await mapStore.worker.profile.location;
770
+
771
+ if ([LocationState.Preset, LocationState.Live].includes(location.source)) {
772
+ mapStore.map.flyTo({
773
+ center: location.coordinates as LngLatLike,
774
+ zoom: 14
775
+ });
776
+ } else {
777
+ throw new Error('No Location Set or Location could not be retrieved');
778
+ }
779
+ }
780
+
781
+ function setLocation() {
782
+ // Always enter manual location setting mode when button is clicked
783
+ mapStore.manualLocationMode = true;
784
+ mode.value = 'SetLocation';
785
+ mapStore.map.getCanvas().style.cursor = 'crosshair';
786
+
787
+ // Store the handler so we can remove it later if needed
788
+ locationClickHandler.value = async (e: MapMouseEvent) => {
789
+ mapStore.map.getCanvas().style.cursor = '';
790
+ mode.value = 'Default';
791
+ locationClickHandler.value = null;
792
+
793
+ await mapStore.worker.profile.update({
794
+ tak_loc: {
795
+ type: 'Point',
796
+ coordinates: [e.lngLat.lng, e.lngLat.lat]
797
+ }
798
+ });
799
+
800
+ await mapStore.refresh();
801
+ };
802
+
803
+ mapStore.map.once('click', locationClickHandler.value);
804
+ }
805
+
806
+ function cancelLocationSetting() {
807
+ mode.value = 'Default';
808
+ mapStore.map.getCanvas().style.cursor = '';
809
+
810
+ // Remove the specific location click handler if it exists
811
+ if (locationClickHandler.value) {
812
+ mapStore.map.off('click', locationClickHandler.value);
813
+ locationClickHandler.value = null;
814
+ }
815
+ }
816
+
817
+ async function exitManualMode() {
818
+ // Switch back to automatic GPS mode
819
+ mapStore.manualLocationMode = false;
820
+ mode.value = 'Default';
821
+ mapStore.map.getCanvas().style.cursor = '';
822
+
823
+ // Remove the specific location click handler if it exists
824
+ if (locationClickHandler.value) {
825
+ mapStore.map.off('click', locationClickHandler.value);
826
+ locationClickHandler.value = null;
827
+ }
828
+
829
+ // Immediately set location to loading state for UI feedback
830
+ mapStore.location = LocationState.Loading;
831
+
832
+ // Remove current location dot from map by removing user's CoT
833
+ const userUid = `ANDROID-CloudTAK-${(await mapStore.worker.profile.load()).username}`;
834
+ await mapStore.worker.db.remove(userUid);
835
+
836
+ // Clear manual location and wait for it to complete
837
+ await mapStore.worker.profile.update({ tak_loc: null });
838
+
839
+ // Restart GPS watch to ensure fresh GPS acquisition
840
+ mapStore.startGPSWatch();
841
+
842
+ await mapStore.refresh();
843
+ }
844
+
845
+
846
+
847
+ function fileUpload(event: string) {
848
+ upload.value.shown = false;
849
+ const imp = JSON.parse(event) as { id: string };
850
+ router.push(`/menu/imports/${imp.id}`)
851
+ }
852
+
853
+ async function handleRadial(event: string): Promise<void> {
854
+ if (!mapStore.radial.cot) return;
855
+ if (!mapStore.radial.cot.properties) mapStore.radial.cot.properties = {};
856
+
857
+ if (event === 'cot:view') {
858
+ router.push(`/cot/${mapStore.radial.cot.properties.id}`);
859
+ closeRadial()
860
+ } else if (event === 'cot:play') {
861
+ await floatStore.addCOT(mapStore.radial.cot.properties.id);
862
+ closeRadial()
863
+ } else if (event === 'cot:delete') {
864
+ const cot = mapStore.radial.cot;
865
+ closeRadial()
866
+
867
+ if (route.name === 'home-menu-cot' && route.params.uid === (cot.properties.id || cot.id)) {
868
+ router.push('/');
869
+ }
870
+
871
+ await mapStore.worker.db.remove(String(cot.properties.id || cot.id), {
872
+ mission: true
873
+ })
874
+
875
+ await mapStore.refresh();
876
+ } else if (event === 'cot:lock') {
877
+ mapStore.locked.push(mapStore.radial.cot.properties ? mapStore.radial.cot.properties.id : mapStore.radial.cot.id);
878
+ closeRadial()
879
+ } else if (event === 'cot:edit') {
880
+ const cot = await mapStore.worker.db.get(mapStore.radial.cot.properties.id || mapStore.radial.cot.id, {
881
+ mission: true
882
+ })
883
+
884
+ if (!cot) throw new Error('Cannot Find COT Marker');
885
+ await mapStore.draw.edit(cot);
886
+
887
+ closeRadial()
888
+ } else if (event === 'feat:view') {
889
+ selectFeat(mapStore.radial.cot as MapGeoJSONFeature);
890
+ closeRadial()
891
+ } else if (event === 'context:new') {
892
+ const feat = toRaw(mapStore.radial.cot as Feature) as Feature;
893
+
894
+ await mapStore.worker.db.add(feat, {
895
+ authored: true
896
+ });
897
+
898
+ await mapStore.refresh();
899
+ closeRadial()
900
+ } else if (event === 'context:info') {
901
+ // @ts-expect-error Figure out geometry.coordinates type
902
+ router.push(`/query/${encodeURIComponent(mapStore.radial.cot.geometry.coordinates.join(','))}`);
903
+ closeRadial()
904
+ } else {
905
+ closeRadial()
906
+ throw new Error(`Unimplemented Radial Action: ${event}`);
907
+ }
908
+ }
909
+
910
+ async function mountMap(): Promise<void> {
911
+ if (!mapRef.value) throw new Error('Map Element could not be found - Please refresh the page and try again');
912
+ await mapStore.init(mapRef.value);
913
+
914
+ return new Promise((resolve) => {
915
+ mapStore.map.once('idle', async () => {
916
+ const profile = await mapStore.worker.profile.load();
917
+
918
+ if (profile.display_projection === 'globe') {
919
+ mapStore.map.setProjection({ type: "globe" });
920
+ }
921
+
922
+ await mapStore.icons.updateImages();
923
+
924
+ await mapStore.initOverlays();
925
+
926
+ timer.value = setInterval(async () => {
927
+ if (!mapStore.map) return;
928
+ await mapStore.refresh();
929
+ }, 500);
930
+
931
+ return resolve();
932
+ });
933
+ });
934
+ }
935
+ </script>
936
+
937
+ <style>
938
+ .maplibregl-ctrl-scale {
939
+ background-color: transparent !important;
940
+ color: #ffffff;
941
+ text-shadow: 1px 0 0 black, -1px 0 0 black, 0 1px 0 black, 0 -1px 0 black;
942
+ border-bottom: 1px solid #fff;
943
+ border-left: 1px solid #fff;
944
+ border-right: 1px solid #fff;
945
+ }
946
+ .maplibregl-ctrl-scale::before {
947
+ background-color: transparent !important;
948
+ border-bottom: 1px solid #000;
949
+ border-left: 1px solid #000;
950
+ border-right: 1px solid #000;
951
+ content: "";
952
+ display: block;
953
+ position: absolute;
954
+ top: 0px;
955
+ left: 1px;
956
+ right: 1px;
957
+ bottom: 1px;
958
+ }
959
+ .maplibregl-ctrl-bottom-left {
960
+ bottom: 0;
961
+ left: 260px;
962
+ }
963
+ .maplibregl-ctrl-bottom-right {
964
+ bottom: 0;
965
+ right: 60px;
966
+ z-index: 1 !important;
967
+ color: black !important;
968
+ }
969
+ .maplibregl-ctrl-attrib a {
970
+ color: black !important;
971
+ }
972
+ </style>