Compare commits
	
		
			9 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b48302b0b7 | |||
| f9a181cdcc | |||
| 6b25c8d7a2 | |||
| 41bbbdc801 | |||
| 2408262170 | |||
| eed553d866 | |||
| 30e4e5292d | |||
| a60d20d76a | |||
| b0d74321c8 | 
							
								
								
									
										16
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# Editor configuration, see https://editorconfig.org
 | 
				
			||||||
 | 
					root = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[*]
 | 
				
			||||||
 | 
					charset = utf-8
 | 
				
			||||||
 | 
					indent_style = tab
 | 
				
			||||||
 | 
					indent_size = 4
 | 
				
			||||||
 | 
					insert_final_newline = true
 | 
				
			||||||
 | 
					trim_trailing_whitespace = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[*.ts]
 | 
				
			||||||
 | 
					quote_type = single
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[*.md]
 | 
				
			||||||
 | 
					max_line_length = off
 | 
				
			||||||
 | 
					trim_trailing_whitespace = false
 | 
				
			||||||
							
								
								
									
										33
									
								
								.github/workflows/functions.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/functions.yaml
									
									
									
									
										vendored
									
									
								
							@@ -2,23 +2,24 @@ name: Build Functions
 | 
				
			|||||||
run-name: Build Functions
 | 
					run-name: Build Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
on:
 | 
					on:
 | 
				
			||||||
  push:
 | 
					    push:
 | 
				
			||||||
    paths:
 | 
					        paths:
 | 
				
			||||||
      - 'functions/**'
 | 
					            - 'firebase/**'
 | 
				
			||||||
 | 
					            - 'functions/**'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					    build:
 | 
				
			||||||
    name: Build NPM Project
 | 
					        name: Build NPM Project
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					        runs-on: ubuntu-latest
 | 
				
			||||||
    container: node
 | 
					        container: node
 | 
				
			||||||
    steps:
 | 
					        steps:
 | 
				
			||||||
      - name: Clone Repository
 | 
					            -   name: Clone Repository
 | 
				
			||||||
        uses: ztimson/actions/clone@develop
 | 
					                uses: ztimson/actions/clone@develop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Install Dependencies
 | 
					            -   name: Install Dependencies
 | 
				
			||||||
        run: npm i
 | 
					                run: npm i
 | 
				
			||||||
        working-directory: functions
 | 
					                working-directory: functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Build Project
 | 
					            -   name: Build Project
 | 
				
			||||||
        run: npm run build
 | 
					                run: npm run build
 | 
				
			||||||
        working-directory: functions
 | 
					                working-directory: functions
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								.github/workflows/website.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								.github/workflows/website.yaml
									
									
									
									
										vendored
									
									
								
							@@ -2,46 +2,55 @@ name: Build Website
 | 
				
			|||||||
run-name: Build Website
 | 
					run-name: Build Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
on:
 | 
					on:
 | 
				
			||||||
  push:
 | 
					    push:
 | 
				
			||||||
    paths-ignore:
 | 
					        paths-ignore:
 | 
				
			||||||
      - 'functions/**'
 | 
					            - 'firebase/**'
 | 
				
			||||||
 | 
					            - 'functions/**'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					    build:
 | 
				
			||||||
    name: Build NPM Project
 | 
					        name: Build NPM Project
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					        runs-on: ubuntu-latest
 | 
				
			||||||
    container: node
 | 
					        container: node
 | 
				
			||||||
    steps:
 | 
					        steps:
 | 
				
			||||||
      - name: Clone Repository
 | 
					            -   name: Clone Repository
 | 
				
			||||||
        uses: ztimson/actions/clone@develop
 | 
					                uses: ztimson/actions/clone@develop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Install Dependencies
 | 
					            -   name: Install Dependencies
 | 
				
			||||||
        run: npm i
 | 
					                run: npm i
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Build Project
 | 
					            -   name: Build Project
 | 
				
			||||||
        run: npm run build
 | 
					                run: npm run build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Upload Artifacts
 | 
					            -   name: Upload Artifacts
 | 
				
			||||||
        if: ${{inputs.artifacts}} != "false"
 | 
					                uses: actions/upload-artifact@v3
 | 
				
			||||||
        uses: actions/upload-artifact@v3
 | 
					                with:
 | 
				
			||||||
 | 
					                    name: website
 | 
				
			||||||
 | 
					                    path: dist
 | 
				
			||||||
 | 
					                    retention-days: 7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tag:
 | 
				
			||||||
 | 
					        name: Tag Version
 | 
				
			||||||
 | 
					        needs: build
 | 
				
			||||||
 | 
					        runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					        container: node
 | 
				
			||||||
 | 
					        steps:
 | 
				
			||||||
 | 
					            -   name: Clone Repository
 | 
				
			||||||
 | 
					                uses: ztimson/actions/clone@develop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            -   name: Get Version Number
 | 
				
			||||||
 | 
					                run: echo "VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            -   name: Tag Version
 | 
				
			||||||
 | 
					                uses: ztimson/actions/tag@develop
 | 
				
			||||||
 | 
					                with:
 | 
				
			||||||
 | 
					                    tag: ${{env.VERSION}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    publish:
 | 
				
			||||||
 | 
					        name: Build & Push Dockerfile
 | 
				
			||||||
 | 
					        needs: build
 | 
				
			||||||
 | 
					        uses: ztimson/actions/.github/workflows/docker.yaml@develop
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          name: website
 | 
					            name: ztimson/map-alliance
 | 
				
			||||||
          path: dist
 | 
					            repository: ${{github.server_url}}/${{github.repository}}.git
 | 
				
			||||||
          retention-days: 7
 | 
					            pass: ${{secrets.DEPLOY_TOKEN}}
 | 
				
			||||||
 | 
					 | 
				
			||||||
  tag:
 | 
					 | 
				
			||||||
    name: Tag Version
 | 
					 | 
				
			||||||
    needs: build
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    container: node
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - name: Clone Repository
 | 
					 | 
				
			||||||
        uses: ztimson/actions/clone@develop
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Get Version Number
 | 
					 | 
				
			||||||
        run: echo "VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')" >> $GITHUB_ENV
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Tag Version
 | 
					 | 
				
			||||||
        uses: ztimson/actions/tag@develop
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          tag: ${{env.VERSION}}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					FROM node as build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Variables
 | 
				
			||||||
 | 
					ARG NODE_ENV=prod
 | 
				
			||||||
 | 
					ARG NODE_OPTIONS="--max_old_space_size=4096"
 | 
				
			||||||
 | 
					ENV NG_CLI_ANALYTICS=ci \
 | 
				
			||||||
 | 
					    NODE_ENV=${NODE_ENV} \
 | 
				
			||||||
 | 
					    NODE_OPTIONS=${NODE_OPTIONS}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Setup
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					COPY . .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Install & build
 | 
				
			||||||
 | 
					RUN if [ ! -d "dist" ]; then npm ci && npm run build; fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Use Nginx to serve
 | 
				
			||||||
 | 
					FROM nginx:1.20-alpine
 | 
				
			||||||
 | 
					COPY --from=build /app/dist/browser /usr/share/nginx/html
 | 
				
			||||||
 | 
					COPY docker/robots.txt /usr/share/nginx/html/robots.txt
 | 
				
			||||||
 | 
					COPY docker/nginx.conf /etc/nginx/nginx.conf
 | 
				
			||||||
 | 
					EXPOSE 80
 | 
				
			||||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							@@ -34,6 +34,7 @@
 | 
				
			|||||||
        - [Demo](#demo)
 | 
					        - [Demo](#demo)
 | 
				
			||||||
        - [Built With](#built-with)
 | 
					        - [Built With](#built-with)
 | 
				
			||||||
    - [Setup](#setup)
 | 
					    - [Setup](#setup)
 | 
				
			||||||
 | 
						    - [Production](#production)
 | 
				
			||||||
        - [Development](#development)
 | 
					        - [Development](#development)
 | 
				
			||||||
    - [License](#license)
 | 
					    - [License](#license)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,11 +53,27 @@ Website: https://maps.zakscode.com
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### Built With
 | 
					### Built With
 | 
				
			||||||
[](https://angular.io/)
 | 
					[](https://angular.io/)
 | 
				
			||||||
 | 
					[](https://docker.com/)
 | 
				
			||||||
[](https://firebase.google.com/)
 | 
					[](https://firebase.google.com/)
 | 
				
			||||||
[](https://typescriptlang.org/)
 | 
					[](https://typescriptlang.org/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Setup
 | 
					## Setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<details>
 | 
				
			||||||
 | 
					<summary>
 | 
				
			||||||
 | 
					  <h3 id="production" style="display: inline">
 | 
				
			||||||
 | 
					    Production
 | 
				
			||||||
 | 
					  </h3>
 | 
				
			||||||
 | 
					</summary>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Prerequisites
 | 
				
			||||||
 | 
					- [Docker](https://docs.docker.com/install/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Instructions
 | 
				
			||||||
 | 
					1. Run the docker image: `docker run -p 80:80 ztimson/map-alliance`
 | 
				
			||||||
 | 
					2. Open http://localhost
 | 
				
			||||||
 | 
					</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<details>
 | 
					<details>
 | 
				
			||||||
<summary>
 | 
					<summary>
 | 
				
			||||||
  <h3 id="development" style="display: inline">
 | 
					  <h3 id="development" style="display: inline">
 | 
				
			||||||
@@ -70,7 +87,7 @@ Website: https://maps.zakscode.com
 | 
				
			|||||||
#### Instructions
 | 
					#### Instructions
 | 
				
			||||||
1. Install the dependencies: `npm install`
 | 
					1. Install the dependencies: `npm install`
 | 
				
			||||||
2. Start the Angular server: `npm run start`
 | 
					2. Start the Angular server: `npm run start`
 | 
				
			||||||
3. Open [http://localhost:4200](http://localhost:4200)
 | 
					3. Open http://localhost:4200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</details>
 | 
					</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										174
									
								
								angular.json
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								angular.json
									
									
									
									
									
								
							@@ -1,91 +1,87 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
 | 
						"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
 | 
				
			||||||
  "version": 1,
 | 
						"version": 1,
 | 
				
			||||||
  "newProjectRoot": "projects",
 | 
						"newProjectRoot": "projects",
 | 
				
			||||||
  "projects": {
 | 
						"projects": {
 | 
				
			||||||
    "MapAlliance": {
 | 
							"proj": {
 | 
				
			||||||
      "projectType": "application",
 | 
								"projectType": "application",
 | 
				
			||||||
      "schematics": {
 | 
								"schematics": {
 | 
				
			||||||
        "@schematics/angular:component": {
 | 
									"@schematics/angular:component": {
 | 
				
			||||||
          "style": "scss"
 | 
										"style": "scss"
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      },
 | 
								},
 | 
				
			||||||
      "root": "",
 | 
								"root": "",
 | 
				
			||||||
      "sourceRoot": "src",
 | 
								"sourceRoot": "src",
 | 
				
			||||||
      "prefix": "app",
 | 
								"prefix": "app",
 | 
				
			||||||
      "architect": {
 | 
								"architect": {
 | 
				
			||||||
        "build": {
 | 
									"build": {
 | 
				
			||||||
          "builder": "@angular-devkit/build-angular:browser",
 | 
										"builder": "@angular-devkit/build-angular:application",
 | 
				
			||||||
          "options": {
 | 
										"options": {
 | 
				
			||||||
            "outputPath": "dist/MapAlliance",
 | 
											"outputPath": "dist",
 | 
				
			||||||
            "index": "src/index.html",
 | 
											"index": "src/index.html",
 | 
				
			||||||
            "main": "src/main.ts",
 | 
											"browser": "src/main.ts",
 | 
				
			||||||
            "polyfills": "src/polyfills.ts",
 | 
											"polyfills": ["zone.js"],
 | 
				
			||||||
            "tsConfig": "tsconfig.json",
 | 
											"tsConfig": "tsconfig.json",
 | 
				
			||||||
            "aot": false,
 | 
											"inlineStyleLanguage": "scss",
 | 
				
			||||||
            "assets": [
 | 
											"assets": [
 | 
				
			||||||
              "src/assets",
 | 
												"src/assets",
 | 
				
			||||||
              "src/manifest.webmanifest"
 | 
												"src/manifest.json"
 | 
				
			||||||
            ],
 | 
											],
 | 
				
			||||||
            "styles": [
 | 
											"styles": [
 | 
				
			||||||
              "./node_modules/bootstrap-scss/bootstrap.scss",
 | 
												"./node_modules/bootstrap-scss/bootstrap.scss",
 | 
				
			||||||
              "./node_modules/leaflet/dist/leaflet.css",
 | 
												"./node_modules/leaflet/dist/leaflet.css",
 | 
				
			||||||
              "src/styles.scss"
 | 
												"src/styles.scss"
 | 
				
			||||||
            ],
 | 
											],
 | 
				
			||||||
            "scripts": [
 | 
											"scripts": [
 | 
				
			||||||
              "./node_modules/leaflet/dist/leaflet.js",
 | 
												"./node_modules/leaflet/dist/leaflet.js",
 | 
				
			||||||
              "./node_modules/leaflet-polylinedecorator/dist/leaflet.polylineDecorator.js",
 | 
												"./node_modules/leaflet-polylinedecorator/dist/leaflet.polylineDecorator.js",
 | 
				
			||||||
              "./node_modules/leaflet-openweathermap/leaflet-openweathermap.js",
 | 
												"./node_modules/leaflet-openweathermap/leaflet-openweathermap.js",
 | 
				
			||||||
              "./node_modules/leaflet-rotatedmarker/leaflet.rotatedMarker.js",
 | 
												"./node_modules/leaflet-rotatedmarker/leaflet.rotatedMarker.js",
 | 
				
			||||||
              "./node_modules/leaflet-bing-layer/leaflet-bing-layer.js",
 | 
												"./node_modules/leaflet-bing-layer/leaflet-bing-layer.js",
 | 
				
			||||||
              "./node_modules/leaflet.gridlayer.googlemutant/Leaflet.GoogleMutant.js",
 | 
												"./node_modules/leaflet.gridlayer.googlemutant/Leaflet.GoogleMutant.js",
 | 
				
			||||||
              "./node_modules/esri-leaflet/dist/esri-leaflet.js",
 | 
												"./node_modules/esri-leaflet/dist/esri-leaflet.js",
 | 
				
			||||||
              "./src/assets/js/leaflet.Editable.js",
 | 
												"./src/assets/js/leaflet.Editable.js",
 | 
				
			||||||
              "./node_modules/jquery/dist/jquery.js"
 | 
												"./node_modules/jquery/dist/jquery.js"
 | 
				
			||||||
            ]
 | 
											]
 | 
				
			||||||
          },
 | 
										},
 | 
				
			||||||
          "configurations": {
 | 
										"configurations": {
 | 
				
			||||||
            "production": {
 | 
											"production": {
 | 
				
			||||||
              "fileReplacements": [
 | 
												"budgets": [
 | 
				
			||||||
                {
 | 
													{
 | 
				
			||||||
                  "replace": "src/environments/environment.ts",
 | 
														"type": "initial",
 | 
				
			||||||
                  "with": "src/environments/environment.prod.ts"
 | 
														"maximumWarning": "2mb",
 | 
				
			||||||
                }
 | 
														"maximumError": "5mb"
 | 
				
			||||||
              ],
 | 
													},
 | 
				
			||||||
              "optimization": true,
 | 
													{
 | 
				
			||||||
              "outputHashing": "all",
 | 
														"type": "anyComponentStyle",
 | 
				
			||||||
              "sourceMap": false,
 | 
														"maximumWarning": "250kb",
 | 
				
			||||||
              "extractCss": true,
 | 
														"maximumError": "1mb"
 | 
				
			||||||
              "namedChunks": false,
 | 
													}
 | 
				
			||||||
              "aot": true,
 | 
												],
 | 
				
			||||||
              "extractLicenses": true,
 | 
												"outputHashing": "all",
 | 
				
			||||||
              "vendorChunk": false,
 | 
												"serviceWorker": "ngsw-config.json"
 | 
				
			||||||
              "buildOptimizer": true,
 | 
											},
 | 
				
			||||||
              "budgets": [
 | 
											"development": {
 | 
				
			||||||
                {
 | 
												"optimization": false,
 | 
				
			||||||
                  "type": "initial",
 | 
												"extractLicenses": false,
 | 
				
			||||||
                  "maximumWarning": "2mb",
 | 
												"sourceMap": true
 | 
				
			||||||
                  "maximumError": "5mb"
 | 
											}
 | 
				
			||||||
                }
 | 
										},
 | 
				
			||||||
              ],
 | 
										"defaultConfiguration": "production"
 | 
				
			||||||
              "serviceWorker": true,
 | 
									},
 | 
				
			||||||
              "ngswConfigPath": "ngsw-config.json"
 | 
									"serve": {
 | 
				
			||||||
            }
 | 
										"builder": "@angular-devkit/build-angular:dev-server",
 | 
				
			||||||
          }
 | 
										"configurations": {
 | 
				
			||||||
        },
 | 
											"production": {
 | 
				
			||||||
        "serve": {
 | 
												"buildTarget": "proj:build:production"
 | 
				
			||||||
          "builder": "@angular-devkit/build-angular:dev-server",
 | 
											},
 | 
				
			||||||
          "options": {
 | 
											"development": {
 | 
				
			||||||
            "browserTarget": "MapAlliance:build"
 | 
												"buildTarget": "proj:build:development"
 | 
				
			||||||
          },
 | 
											}
 | 
				
			||||||
          "configurations": {
 | 
										},
 | 
				
			||||||
            "production": {
 | 
										"defaultConfiguration": "development"
 | 
				
			||||||
              "browserTarget": "MapAlliance:build:production"
 | 
									}
 | 
				
			||||||
            }
 | 
								}
 | 
				
			||||||
          }
 | 
							}
 | 
				
			||||||
        }
 | 
						}
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "defaultProject": "MapAlliance"
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								browserslist
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								browserslist
									
									
									
									
									
								
							@@ -1,12 +0,0 @@
 | 
				
			|||||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
 | 
					 | 
				
			||||||
# For additional information regarding the format and rule options, please see:
 | 
					 | 
				
			||||||
# https://github.com/browserslist/browserslist#queries
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# You can see what browsers were selected by your queries by running:
 | 
					 | 
				
			||||||
#   npx browserslist
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 0.5%
 | 
					 | 
				
			||||||
last 2 versions
 | 
					 | 
				
			||||||
Firefox ESR
 | 
					 | 
				
			||||||
not dead
 | 
					 | 
				
			||||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
 | 
					 | 
				
			||||||
							
								
								
									
										31
									
								
								docker/nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								docker/nginx.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					worker_processes  auto;
 | 
				
			||||||
 | 
					pid /var/run/nginx.pid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					events {
 | 
				
			||||||
 | 
					    worker_connections  1024;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					http {
 | 
				
			||||||
 | 
					  include       mime.types;
 | 
				
			||||||
 | 
					  default_type  application/octet-stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gzip on;
 | 
				
			||||||
 | 
					  gzip_proxied any;
 | 
				
			||||||
 | 
					  gzip_types text/plain text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript application/json application/x-font-woff;
 | 
				
			||||||
 | 
					  gzip_vary on;
 | 
				
			||||||
 | 
					  gzip_disable "MSIE [1-6]\.(?!.*SV1)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sendfile off;
 | 
				
			||||||
 | 
					  keepalive_timeout 65;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  server {
 | 
				
			||||||
 | 
					    listen 80;
 | 
				
			||||||
 | 
					    index index.html;
 | 
				
			||||||
 | 
					    root /usr/share/nginx/html;
 | 
				
			||||||
 | 
					    autoindex off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    location / {
 | 
				
			||||||
 | 
					      try_files $uri $uri/ /index.html;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								docker/robots.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								docker/robots.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					User-Agent: *
 | 
				
			||||||
 | 
					Allow: /
 | 
				
			||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "hosting": {
 | 
					  "hosting": {
 | 
				
			||||||
    "public": "dist/MapAlliance",
 | 
					    "public": "dist/browser",
 | 
				
			||||||
    "ignore": [
 | 
					    "ignore": [
 | 
				
			||||||
 | 
					      ".*",
 | 
				
			||||||
      "firebase.json",
 | 
					      "firebase.json",
 | 
				
			||||||
      "**/.*",
 | 
					      "node_modules"
 | 
				
			||||||
      "**/node_modules/**"
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "rewrites": [
 | 
					    "rewrites": [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
@@ -14,12 +14,11 @@
 | 
				
			|||||||
    ]
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "firestore": {
 | 
					  "firestore": {
 | 
				
			||||||
    "rules": "firestore.rules",
 | 
					    "rules": "firebase/firestore.rules",
 | 
				
			||||||
    "indexes": "firestore.indexes.json"
 | 
					    "indexes": "firebase/firestore.indexes.json"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "functions": {
 | 
					  "functions": {
 | 
				
			||||||
    "predeploy": [
 | 
					    "predeploy": [
 | 
				
			||||||
      "npm --prefix \"$RESOURCE_DIR\" run lint",
 | 
					 | 
				
			||||||
      "npm --prefix \"$RESOURCE_DIR\" run build"
 | 
					      "npm --prefix \"$RESOURCE_DIR\" run build"
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,20 +7,21 @@
 | 
				
			|||||||
      "installMode": "prefetch",
 | 
					      "installMode": "prefetch",
 | 
				
			||||||
      "resources": {
 | 
					      "resources": {
 | 
				
			||||||
        "files": [
 | 
					        "files": [
 | 
				
			||||||
          "/favicon.ico",
 | 
					          "/assets/images/logo.png",
 | 
				
			||||||
          "/index.html",
 | 
					          "/index.html",
 | 
				
			||||||
 | 
					          "/manifest.json",
 | 
				
			||||||
          "/*.css",
 | 
					          "/*.css",
 | 
				
			||||||
          "/*.js"
 | 
					          "/*.js"
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }, {
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      "name": "assets",
 | 
					      "name": "assets",
 | 
				
			||||||
      "installMode": "lazy",
 | 
					      "installMode": "lazy",
 | 
				
			||||||
      "updateMode": "prefetch",
 | 
					      "updateMode": "prefetch",
 | 
				
			||||||
      "resources": {
 | 
					      "resources": {
 | 
				
			||||||
        "files": [
 | 
					        "files": [
 | 
				
			||||||
          "/assets/**",
 | 
					          "/assets/**"
 | 
				
			||||||
          "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
 | 
					 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20535
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										20535
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										111
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								package.json
									
									
									
									
									
								
							@@ -1,64 +1,51 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "map-alliance",
 | 
						"name": "map-alliance",
 | 
				
			||||||
  "version": "1.7.8",
 | 
						"version": "2.0.0",
 | 
				
			||||||
  "scripts": {
 | 
						"scripts": {
 | 
				
			||||||
    "ng": "ng",
 | 
							"start": "ng serve",
 | 
				
			||||||
    "start": "ng serve --host 0.0.0.0",
 | 
							"build": "ng build",
 | 
				
			||||||
    "build": "ng build",
 | 
							"watch": "ng build --watch --configuration development",
 | 
				
			||||||
    "test": "ng test",
 | 
							"deploy": "firebase deploy --only hosting --token \"${FIREBASE_TOKEN}\""
 | 
				
			||||||
    "lint": "ng lint",
 | 
						},
 | 
				
			||||||
    "e2e": "ng e2e",
 | 
						"private": true,
 | 
				
			||||||
    "deploy": "firebase deploy --only hosting --token \"${FIREBASE_TOKEN}\""
 | 
						"dependencies": {
 | 
				
			||||||
  },
 | 
							"@angular/animations": "^17.0.0",
 | 
				
			||||||
  "private": true,
 | 
							"@angular/cdk": "^17.0.0",
 | 
				
			||||||
  "dependencies": {
 | 
							"@angular/common": "^17.0.0",
 | 
				
			||||||
    "@angular/animations": "~9.1.11",
 | 
							"@angular/compiler": "^17.0.0",
 | 
				
			||||||
    "@angular/cdk": "^9.2.4",
 | 
							"@angular/core": "^17.0.0",
 | 
				
			||||||
    "@angular/common": "~9.1.11",
 | 
							"@angular/fire": "^17.0.0",
 | 
				
			||||||
    "@angular/compiler": "~9.1.11",
 | 
							"@angular/forms": "^17.0.0",
 | 
				
			||||||
    "@angular/core": "~9.1.11",
 | 
							"@angular/material": "^17.0.0",
 | 
				
			||||||
    "@angular/fire": "^5.2.1",
 | 
							"@angular/platform-browser": "^17.0.0",
 | 
				
			||||||
    "@angular/forms": "~9.1.11",
 | 
							"@angular/platform-browser-dynamic": "^17.0.0",
 | 
				
			||||||
    "@angular/material": "^9.2.4",
 | 
							"@angular/pwa": "^17.0.0",
 | 
				
			||||||
    "@angular/platform-browser": "~9.1.11",
 | 
							"@angular/router": "^17.0.0",
 | 
				
			||||||
    "@angular/platform-browser-dynamic": "~9.1.11",
 | 
							"@angular/service-worker": "^17.0.0",
 | 
				
			||||||
    "@angular/pwa": "^0.800.4",
 | 
							"bootstrap-scss": "^4.3.1",
 | 
				
			||||||
    "@angular/router": "~9.1.11",
 | 
							"classlist.js": "^1.1.20150312",
 | 
				
			||||||
    "@angular/service-worker": "~9.1.11",
 | 
							"esri-leaflet": "^2.3.0",
 | 
				
			||||||
    "@types/leaflet": "^1.5.1",
 | 
							"firebase": "^10.7.0",
 | 
				
			||||||
    "@types/lodash": "^4.14.138",
 | 
							"hammerjs": "^2.0.8",
 | 
				
			||||||
    "bootstrap-scss": "^4.3.1",
 | 
							"jquery": "^3.4.1",
 | 
				
			||||||
    "classlist.js": "^1.1.20150312",
 | 
							"leaflet": "^1.5.1",
 | 
				
			||||||
    "esri-leaflet": "^2.3.0",
 | 
							"leaflet-bing-layer": "^3.3.1",
 | 
				
			||||||
    "firebase": "^6.3.0",
 | 
							"leaflet-openweathermap": "^1.0.0",
 | 
				
			||||||
    "hammerjs": "^2.0.8",
 | 
							"leaflet-polylinedecorator": "^1.6.0",
 | 
				
			||||||
    "jquery": "^3.4.1",
 | 
							"leaflet-rotatedmarker": "^0.2.0",
 | 
				
			||||||
    "leaflet": "^1.5.1",
 | 
							"leaflet.gridlayer.googlemutant": "^0.8.0",
 | 
				
			||||||
    "leaflet-bing-layer": "^3.3.1",
 | 
							"ng-click-outside": "^9.0.0",
 | 
				
			||||||
    "leaflet-openweathermap": "^1.0.0",
 | 
							"ngx-color-picker": "^16.0.0",
 | 
				
			||||||
    "leaflet-polylinedecorator": "^1.6.0",
 | 
							"rxjs": "~7.8.0",
 | 
				
			||||||
    "leaflet-rotatedmarker": "^0.2.0",
 | 
							"tslib": "^2.3.0",
 | 
				
			||||||
    "leaflet.gridlayer.googlemutant": "^0.8.0",
 | 
							"zone.js": "~0.14.2"
 | 
				
			||||||
    "lodash": "^4.17.15",
 | 
						},
 | 
				
			||||||
    "momentjs": "^2.0.0",
 | 
						"devDependencies": {
 | 
				
			||||||
    "ng-click-outside": "^5.0.0",
 | 
							"@angular-devkit/build-angular": "^17.0.6",
 | 
				
			||||||
    "ngx-color-picker": "^8.1.0",
 | 
							"@angular/cli": "^17.0.0",
 | 
				
			||||||
    "rxjs": "~6.5.5",
 | 
							"@angular/compiler-cli": "^17.0.0",
 | 
				
			||||||
    "ts-md5": "^1.2.5",
 | 
							"@types/leaflet": "^1.5.1",
 | 
				
			||||||
    "tslib": "^1.13.0",
 | 
							"firebase-tools": "^12.0.0",
 | 
				
			||||||
    "web-animations-js": "^2.3.2",
 | 
							"typescript": "~5.2.2"
 | 
				
			||||||
    "whatwg-fetch": "^3.0.0",
 | 
						}
 | 
				
			||||||
    "zone.js": "~0.10.3"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "devDependencies": {
 | 
					 | 
				
			||||||
    "@angular-devkit/build-angular": "~0.901.8",
 | 
					 | 
				
			||||||
    "@angular/cli": "~9.1.8",
 | 
					 | 
				
			||||||
    "@angular/compiler-cli": "~9.1.11",
 | 
					 | 
				
			||||||
    "@angular/language-service": "~9.1.11",
 | 
					 | 
				
			||||||
    "@types/node": "~8.9.4",
 | 
					 | 
				
			||||||
    "firebase-tools": "^7.0.2",
 | 
					 | 
				
			||||||
    "ts-node": "~7.0.0",
 | 
					 | 
				
			||||||
    "tslint": "~5.15.0",
 | 
					 | 
				
			||||||
    "typescript": "~3.8.3"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,14 @@ import {SwUpdate} from "@angular/service-worker";
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AppComponent {
 | 
					export class AppComponent {
 | 
				
			||||||
    constructor(private snackbar: MatSnackBar, private update: SwUpdate) {
 | 
					    constructor(private snackbar: MatSnackBar, private update: SwUpdate) {
 | 
				
			||||||
        update.available.subscribe(() => snackbar.open('Update Available!! 🚀', 'Reload').onAction().subscribe(async () => update.activateUpdate()))
 | 
							// Check for updates
 | 
				
			||||||
        update.activated.subscribe(() => window.location.reload());
 | 
							(async () => {
 | 
				
			||||||
 | 
								if(await update.checkForUpdate())
 | 
				
			||||||
 | 
									snackbar.open('Update Available!! 🚀', 'Reload')
 | 
				
			||||||
 | 
										.onAction().subscribe(async () => {
 | 
				
			||||||
 | 
											await update.activateUpdate();
 | 
				
			||||||
 | 
											location.reload();
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
							})();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,58 +1,51 @@
 | 
				
			|||||||
import {NgModule} from "@angular/core";
 | 
					import {isDevMode, NgModule} from "@angular/core";
 | 
				
			||||||
import {AppRouting} from './app.routing';
 | 
					import {FormsModule} from "@angular/forms";
 | 
				
			||||||
 | 
					import {BrowserModule} from "@angular/platform-browser";
 | 
				
			||||||
 | 
					import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
 | 
				
			||||||
 | 
					import {ServiceWorkerModule} from "@angular/service-worker";
 | 
				
			||||||
 | 
					import {ClickOutsideModule} from "ng-click-outside";
 | 
				
			||||||
 | 
					import {ColorPickerModule} from "ngx-color-picker";
 | 
				
			||||||
import {AppComponent} from './app.component';
 | 
					import {AppComponent} from './app.component';
 | 
				
			||||||
import {environment} from '../environments/environment';
 | 
					import {AppRouting} from './app.routing';
 | 
				
			||||||
import {MapComponent} from "./views/map/map.component";
 | 
					 | 
				
			||||||
import {HomeComponent} from "./views/home/home.component";
 | 
					 | 
				
			||||||
import {MaterialModule} from "./material.module";
 | 
					 | 
				
			||||||
import {CalibrateComponent} from "./components/calibrate/calibrate.component";
 | 
					import {CalibrateComponent} from "./components/calibrate/calibrate.component";
 | 
				
			||||||
import {PermissionsComponent} from "./components/permissions/permissions.component";
 | 
					 | 
				
			||||||
import {ToolbarComponent} from "./components/toolbar/toolbar.component";
 | 
					 | 
				
			||||||
import {PaletteComponent} from "./components/palette/palette.component"
 | 
					 | 
				
			||||||
import {ColorPickerDialogComponent} from "./components/colorPickerDialog/colorPickerDialog.component";
 | 
					import {ColorPickerDialogComponent} from "./components/colorPickerDialog/colorPickerDialog.component";
 | 
				
			||||||
import {DimensionsDialogComponent} from "./components/dimensionsDialog/dimensionsDialog.component";
 | 
					import {DimensionsDialogComponent} from "./components/dimensionsDialog/dimensionsDialog.component";
 | 
				
			||||||
import {EditSymbolComponent} from "./components/editSymbol/editSymbol.component";
 | 
					import {EditSymbolComponent} from "./components/editSymbol/editSymbol.component";
 | 
				
			||||||
import {AngularFireModule} from "@angular/fire";
 | 
					import {PaletteComponent} from "./components/palette/palette.component";
 | 
				
			||||||
import {AngularFirestoreModule} from "@angular/fire/firestore";
 | 
					import {PermissionsComponent} from "./components/permissions/permissions.component";
 | 
				
			||||||
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
 | 
					 | 
				
			||||||
import {BrowserModule} from "@angular/platform-browser";
 | 
					 | 
				
			||||||
import {ClickOutsideModule} from "ng-click-outside";
 | 
					 | 
				
			||||||
import {ColorPickerModule} from "ngx-color-picker";
 | 
					 | 
				
			||||||
import {FormsModule} from "@angular/forms";
 | 
					 | 
				
			||||||
import {ServiceWorkerModule} from "@angular/service-worker";
 | 
					 | 
				
			||||||
import {StarrySkyComponent} from "./components/starrySky/starrySky.component";
 | 
					import {StarrySkyComponent} from "./components/starrySky/starrySky.component";
 | 
				
			||||||
import {AngularFireAuthModule} from "@angular/fire/auth";
 | 
					import {ToolbarComponent} from "./components/toolbar/toolbar.component";
 | 
				
			||||||
 | 
					import {MaterialModule} from "./material.module";
 | 
				
			||||||
 | 
					import {HomeComponent} from "./views/home/home.component";
 | 
				
			||||||
 | 
					import {MapComponent} from "./views/map/map.component";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
    declarations: [
 | 
						declarations: [
 | 
				
			||||||
        AppComponent,
 | 
							AppComponent,
 | 
				
			||||||
        CalibrateComponent,
 | 
							CalibrateComponent,
 | 
				
			||||||
        ColorPickerDialogComponent,
 | 
							ColorPickerDialogComponent,
 | 
				
			||||||
        DimensionsDialogComponent,
 | 
							DimensionsDialogComponent,
 | 
				
			||||||
        EditSymbolComponent,
 | 
							EditSymbolComponent,
 | 
				
			||||||
        HomeComponent,
 | 
							HomeComponent,
 | 
				
			||||||
        MapComponent,
 | 
							MapComponent,
 | 
				
			||||||
        PaletteComponent,
 | 
							PaletteComponent,
 | 
				
			||||||
        PermissionsComponent,
 | 
							PermissionsComponent,
 | 
				
			||||||
        StarrySkyComponent,
 | 
							StarrySkyComponent,
 | 
				
			||||||
        ToolbarComponent
 | 
							ToolbarComponent
 | 
				
			||||||
    ],
 | 
						],
 | 
				
			||||||
    imports: [
 | 
						imports: [
 | 
				
			||||||
        AngularFireModule.initializeApp(environment.firebaseConfig),
 | 
							AppRouting,
 | 
				
			||||||
        AngularFirestoreModule.enablePersistence(),
 | 
							BrowserAnimationsModule,
 | 
				
			||||||
        AngularFireAuthModule,
 | 
							BrowserModule,
 | 
				
			||||||
        AppRouting,
 | 
							FormsModule,
 | 
				
			||||||
        BrowserAnimationsModule,
 | 
							ClickOutsideModule,
 | 
				
			||||||
        BrowserModule,
 | 
							ColorPickerModule,
 | 
				
			||||||
        ClickOutsideModule,
 | 
							MaterialModule,
 | 
				
			||||||
        ColorPickerModule,
 | 
							ServiceWorkerModule.register('ngsw-worker.js', {
 | 
				
			||||||
        FormsModule,
 | 
								enabled: !isDevMode(),
 | 
				
			||||||
        MaterialModule,
 | 
								registrationStrategy: 'registerWhenStable:30000' // when stable or after 30 seconds
 | 
				
			||||||
        ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}),
 | 
							})
 | 
				
			||||||
    ],
 | 
						],
 | 
				
			||||||
    providers: [],
 | 
						bootstrap: [AppComponent]
 | 
				
			||||||
    entryComponents: [CalibrateComponent, ColorPickerDialogComponent, DimensionsDialogComponent, EditSymbolComponent, PermissionsComponent],
 | 
					 | 
				
			||||||
    bootstrap: [AppComponent]
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppModule {
 | 
					export class AppModule {}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,33 @@
 | 
				
			|||||||
<div>
 | 
					<div class="pt-2">
 | 
				
			||||||
    <mat-button-toggle-group class="mb-2" (change)="physicsService.mode = $event.value">
 | 
						<mat-button-toggle-group class="mb-2" (change)="physicsService.mode = $event.value">
 | 
				
			||||||
        <mat-button-toggle value="gps" [checked]="physicsService.mode == 'gps'">GPS</mat-button-toggle>
 | 
							<mat-button-toggle value="gps" [checked]="physicsService.mode == 'gps'">GPS</mat-button-toggle>
 | 
				
			||||||
        <mat-button-toggle value="orientation" [checked]="physicsService.mode == 'orientation'">Compass</mat-button-toggle>
 | 
							<mat-button-toggle value="orientation" [checked]="physicsService.mode == 'orientation'">Compass</mat-button-toggle>
 | 
				
			||||||
    </mat-button-toggle-group>
 | 
						</mat-button-toggle-group>
 | 
				
			||||||
    <div *ngIf="physicsService.mode == 'orientation'" [@expand] [@collapse]>
 | 
						<div *ngIf="physicsService.mode == 'orientation'" [@expand] [@collapse]>
 | 
				
			||||||
        <div class="row mt-2">
 | 
							<div class="row mt-2">
 | 
				
			||||||
            <div class="col-5 pr-0">
 | 
								<div class="col-5 pr-0">
 | 
				
			||||||
                <mat-form-field appearance="fill" class="w-100">
 | 
									<mat-form-field appearance="fill" class="w-100">
 | 
				
			||||||
                    <mat-label>Shift +/-</mat-label>
 | 
										<mat-label>Shift +/-</mat-label>
 | 
				
			||||||
                    <input matInput type="number" min="-180" max="180" [(ngModel)]="calibration">
 | 
										<input matInput type="number" min="-180" max="180" [(ngModel)]="calibration">
 | 
				
			||||||
                </mat-form-field>
 | 
									</mat-form-field>
 | 
				
			||||||
            </div>
 | 
								</div>
 | 
				
			||||||
            <div class="col-2 text-center"><h1> = </h1></div>
 | 
								<div class="col-2 text-center"><h1> = </h1></div>
 | 
				
			||||||
            <div class="col-5 pl-0">
 | 
								<div class="col-5 pl-0">
 | 
				
			||||||
                <mat-form-field appearance="fill" class="w-100">
 | 
									<mat-form-field appearance="fill" class="w-100">
 | 
				
			||||||
                    <mat-label>Heading</mat-label>
 | 
										<mat-label>Heading</mat-label>
 | 
				
			||||||
                    <input matInput type="number" [value]="(physicsService.info | async)?.heading | number : '1.0-0'" readonly>
 | 
										<input matInput type="number" [value]="(physicsService.info | async)?.heading | number : '1.0-0'" readonly>
 | 
				
			||||||
                </mat-form-field>
 | 
									</mat-form-field>
 | 
				
			||||||
            </div>
 | 
								</div>
 | 
				
			||||||
        </div>
 | 
							</div>
 | 
				
			||||||
        <div>
 | 
							<div>
 | 
				
			||||||
            <mat-slider class="w-100" [max]="180" [min]="-180" [step]="1" [tickInterval]="90" [thumbLabel]="true" [ngModel]="calibration" (input)="calibration = $event.value"></mat-slider>
 | 
								<mat-slider style="width: 96%" [min]="-180" [max]="180" [step]="1" color="accent" showTickMarks discrete>
 | 
				
			||||||
        </div>
 | 
									<input [(ngModel)]="calibration" matSliderThumb>
 | 
				
			||||||
    </div>
 | 
								</mat-slider>
 | 
				
			||||||
    <mat-divider class="mb-1"></mat-divider>
 | 
							</div>
 | 
				
			||||||
    <div>
 | 
						</div>
 | 
				
			||||||
        <button mat-button *ngIf="physicsService.mode == 'orientation'" class="float-left" (click)="setN()">Set 0°</button>
 | 
						<mat-divider class="mb-1"></mat-divider>
 | 
				
			||||||
        <button mat-button class="float-right" (click)="close()">Close</button>
 | 
						<div>
 | 
				
			||||||
    </div>
 | 
							<button mat-button *ngIf="physicsService.mode == 'orientation'" class="float-left" (click)="setN()">Set 0°</button>
 | 
				
			||||||
 | 
							<button mat-button class="float-right" (click)="close()">Close</button>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,35 +1,40 @@
 | 
				
			|||||||
import {Component} from "@angular/core";
 | 
					import {Component} from "@angular/core";
 | 
				
			||||||
import {MatBottomSheetRef} from "@angular/material/bottom-sheet";
 | 
					import {MatBottomSheetRef} from "@angular/material/bottom-sheet";
 | 
				
			||||||
import {PhysicsService} from "../../services/physics.service";
 | 
					import {PhysicsService} from "../../services/physics.service";
 | 
				
			||||||
import {collapse, expand} from "../../animations";
 | 
					import {collapse, expand} from "../../utils/animations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
    selector: 'calibrate',
 | 
						selector: 'calibrate',
 | 
				
			||||||
    templateUrl: 'calibrate.component.html',
 | 
						templateUrl: 'calibrate.component.html',
 | 
				
			||||||
    animations: [collapse, expand]
 | 
						animations: [collapse, expand]
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class CalibrateComponent {
 | 
					export class CalibrateComponent {
 | 
				
			||||||
    private _calibration = 0;
 | 
						private _calibration = 0;
 | 
				
			||||||
    get calibration() { return this._calibration; }
 | 
						get calibration() { return this._calibration; }
 | 
				
			||||||
    set calibration(c: number) {
 | 
					 | 
				
			||||||
        this._calibration = c;
 | 
					 | 
				
			||||||
        this.physicsService.calibrate.next(c);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private bottomSheetRef: MatBottomSheetRef, public physicsService: PhysicsService) {
 | 
						set calibration(c: number) {
 | 
				
			||||||
        this._calibration = this.physicsService.calibrate.value;
 | 
							this._calibration = c;
 | 
				
			||||||
    }
 | 
							this.physicsService.calibrate.next(c);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    close() {
 | 
						constructor(private bottomSheetRef: MatBottomSheetRef, public physicsService: PhysicsService) {
 | 
				
			||||||
        this.bottomSheetRef.dismiss();
 | 
							this._calibration = this.physicsService.calibrate.value;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setN() {
 | 
						close() {
 | 
				
			||||||
        let currentHeading = Math.round(this.physicsService.orientation.value.alpha);
 | 
							this.bottomSheetRef.dismiss();
 | 
				
			||||||
        if(currentHeading > 0) {
 | 
						}
 | 
				
			||||||
            this.calibration = currentHeading > 180 ? currentHeading - 360 : currentHeading;
 | 
					
 | 
				
			||||||
        } else {
 | 
						setCalibration(target: any) {
 | 
				
			||||||
            this.calibration = -currentHeading;
 | 
							this.calibration = target.value;
 | 
				
			||||||
        }
 | 
						}
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
 | 
						setN() {
 | 
				
			||||||
 | 
							let currentHeading = Math.round(this.physicsService.orientation.value.alpha ?? 0);
 | 
				
			||||||
 | 
							if(currentHeading > 0) {
 | 
				
			||||||
 | 
								this.calibration = currentHeading > 180 ? currentHeading - 360 : currentHeading;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								this.calibration = -currentHeading;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <form id="dimensionsForm" class="d-flex align-items-center" (ngSubmit)="close()">
 | 
					    <form id="dimensionsForm" class="d-flex align-items-center pt-4 px-4 pb-0" (ngSubmit)="close()">
 | 
				
			||||||
        <ng-container *ngFor="let d of dimensions; let i = index">
 | 
					        <ng-container *ngFor="let d of dimensions; let i = index">
 | 
				
			||||||
            <div class="flex-shrink-0 mx-2" *ngIf="i != 0">X</div>
 | 
					            <div class="flex-shrink-0 mb-2 mx-2" *ngIf="i != 0">X</div>
 | 
				
			||||||
            <div class="flex-shrink-1">
 | 
					            <div class="flex-shrink-1">
 | 
				
			||||||
                <mat-form-field class='w-100' appearance="fill">
 | 
					                <mat-form-field class='w-100' appearance="fill">
 | 
				
			||||||
                    <mat-label>{{d}}</mat-label>
 | 
					                    <mat-label>{{d}}</mat-label>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
::ng-deep .mat-form-field-infix {
 | 
					.mat-form-field-infix {
 | 
				
			||||||
  width: auto !important;
 | 
					  width: auto !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,75 +1,75 @@
 | 
				
			|||||||
@function stars ($n) {
 | 
					@function stars ($n) {
 | 
				
			||||||
  $value: '#{random(2000)}px #{random(2000)}px #FFF';
 | 
						$value: '#{random(4000)}px #{random(4000)}px #ffffff';
 | 
				
			||||||
  @for $i from 2 through $n {
 | 
						@for $i from 2 through $n {
 | 
				
			||||||
    $value: '#{$value} , #{random(2000)}px #{random(2000)}px #FFF';
 | 
							$value: '#{$value} , #{random(4000)}px #{random(4000)}px #ffffff';
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
  @return unquote($value)
 | 
						@return unquote($value)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.sky {
 | 
					.sky {
 | 
				
			||||||
  height: 100%;
 | 
						height: 100%;
 | 
				
			||||||
  width: 100%;
 | 
						width: 100%;
 | 
				
			||||||
  background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%);
 | 
						background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%);
 | 
				
			||||||
  overflow: hidden
 | 
						overflow: hidden
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.shooting-star {
 | 
					.shooting-star {
 | 
				
			||||||
  position: absolute;
 | 
						position: absolute;
 | 
				
			||||||
  top: 110%;
 | 
						top: 110%;
 | 
				
			||||||
  width: 30px;
 | 
						width: 30px;
 | 
				
			||||||
  height: 3px;
 | 
						height: 3px;
 | 
				
			||||||
  border-radius: 50%;
 | 
						border-radius: 50%;
 | 
				
			||||||
  background-color: white;
 | 
						background-color: white;
 | 
				
			||||||
  animation: shoot 20s linear infinite;
 | 
						animation: shoot 20s linear infinite;
 | 
				
			||||||
  animation-delay: 1s;
 | 
						animation-delay: 1s;
 | 
				
			||||||
  transform: rotate(-30deg);
 | 
						transform: rotate(-40deg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.stars {
 | 
					.stars {
 | 
				
			||||||
  background: transparent;
 | 
						background: transparent;
 | 
				
			||||||
  border-radius: 50%;
 | 
						border-radius: 50%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.foreground {
 | 
						&.foreground {
 | 
				
			||||||
    width: 1px;
 | 
							width: 1px;
 | 
				
			||||||
    height: 1px;
 | 
							height: 1px;
 | 
				
			||||||
    box-shadow: stars(1000);
 | 
							box-shadow: stars(4000);
 | 
				
			||||||
    animation: spin 200s linear infinite;
 | 
							animation: spin 200s linear infinite;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.midground {
 | 
						&.midground {
 | 
				
			||||||
    width: 2px;
 | 
							width: 2px;
 | 
				
			||||||
    height: 2px;
 | 
							height: 2px;
 | 
				
			||||||
    box-shadow: stars(750);
 | 
							box-shadow: stars(2000);
 | 
				
			||||||
    animation: spin 300s linear infinite;
 | 
							animation: spin 300s linear infinite;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.background {
 | 
						&.background {
 | 
				
			||||||
    width: 4px;
 | 
							width: 4px;
 | 
				
			||||||
    height: 4px;
 | 
							height: 4px;
 | 
				
			||||||
    box-shadow: stars(500);
 | 
							box-shadow: stars(1000);
 | 
				
			||||||
    animation: spin 350s linear infinite;
 | 
							animation: spin 350s linear infinite;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@keyframes shoot{
 | 
					@keyframes shoot {
 | 
				
			||||||
  0%{
 | 
						0% {
 | 
				
			||||||
    top: -10%;
 | 
							top: -10%;
 | 
				
			||||||
    left: 80%;
 | 
							left: 80%;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
  5%{
 | 
						5% {
 | 
				
			||||||
    top: 110%;
 | 
							top: 110%;
 | 
				
			||||||
    left: 20%;
 | 
							left: 20%;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
  100% {
 | 
						100% {
 | 
				
			||||||
    top: 110%;
 | 
							top: 110%;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@keyframes spin {
 | 
					@keyframes spin {
 | 
				
			||||||
  from {
 | 
						from {
 | 
				
			||||||
    transform: translateY(0);
 | 
							transform: translateY(0);
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
  to {
 | 
						to {
 | 
				
			||||||
    transform: translateY(-2000px);
 | 
							transform: translateY(-2000px);
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<mat-toolbar id="toolbar">
 | 
					<mat-toolbar id="toolbar">
 | 
				
			||||||
    <button mat-icon-button routerLink="/">
 | 
					    <button mat-icon-button class="p-0" routerLink="/">
 | 
				
			||||||
        <img src="/assets/images/logo.png" height="35px" width="auto">
 | 
					        <img src="/assets/images/logo.png" style="height: 36px; width: 36px">
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
    <small class="ml-1">{{version}}</small>
 | 
					    <small class="ml-1">{{version}}</small>
 | 
				
			||||||
    <mat-progress-spinner *ngIf="(status | async) == 'staving'" color="primary" class="text-muted pl-2"></mat-progress-spinner>
 | 
					    <mat-progress-spinner *ngIf="(status | async) == 'staving'" color="primary" class="text-muted pl-2"></mat-progress-spinner>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,22 @@
 | 
				
			|||||||
.selected {
 | 
					.mat-toolbar {
 | 
				
			||||||
  background-color: rgba(70, 70, 70, 0.8);
 | 
						position: absolute;
 | 
				
			||||||
 | 
						height: 45px !important;
 | 
				
			||||||
 | 
						z-index: 5000;
 | 
				
			||||||
 | 
						color: rgba(255, 255, 255, 0.54);
 | 
				
			||||||
 | 
						background-color: rgba(0, 0, 0, 0.8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.selected {
 | 
				
			||||||
 | 
							background-color: rgba(70, 70, 70, 0.8);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.mat-mdc-icon-button {
 | 
				
			||||||
 | 
							height: 40px;
 | 
				
			||||||
 | 
							width: 40px;
 | 
				
			||||||
 | 
							padding: 8px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:hover {
 | 
				
			||||||
 | 
								background-color: rgba(90, 90, 90, 0.8) !important;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,2 +1 @@
 | 
				
			|||||||
export interface Map {
 | 
					export interface Map { }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import {User as FirebaseUser} from "firebase"
 | 
					import {User as FirebaseUser} from '@angular/fire/auth'
 | 
				
			||||||
import {AngularFirestoreDocument} from '@angular/fire/firestore';
 | 
					import {DocumentReference} from '@angular/fire/firestore';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface User extends FirebaseUser {
 | 
					export interface User extends FirebaseUser {
 | 
				
			||||||
    ref?: AngularFirestoreDocument;
 | 
					    ref?: DocumentReference;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,46 +1,54 @@
 | 
				
			|||||||
import {Injectable} from "@angular/core";
 | 
					import {Injectable} from '@angular/core';
 | 
				
			||||||
import {BehaviorSubject, from} from "rxjs";
 | 
					import {BehaviorSubject} from 'rxjs';
 | 
				
			||||||
import {AngularFirestore} from "@angular/fire/firestore";
 | 
					import {Router} from '@angular/router';
 | 
				
			||||||
import {AngularFireAuth} from "@angular/fire/auth";
 | 
					 | 
				
			||||||
import {auth} from 'firebase';
 | 
					 | 
				
			||||||
import {Router} from "@angular/router";
 | 
					 | 
				
			||||||
import {flatMap, map, skip} from 'rxjs/operators';
 | 
					 | 
				
			||||||
import {User} from '../models/user';
 | 
					import {User} from '../models/user';
 | 
				
			||||||
 | 
					import {getAuth, FacebookAuthProvider, GoogleAuthProvider, signInWithPopup} from 'firebase/auth';
 | 
				
			||||||
 | 
					import {collection, getFirestore, doc, getDoc} from 'firebase/firestore';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
    providedIn: 'root'
 | 
						providedIn: 'root'
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AuthService {
 | 
					export class AuthService {
 | 
				
			||||||
    readonly collection = 'Users';
 | 
						readonly collection = 'Users';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    authenticated = false;
 | 
						authenticated = false;
 | 
				
			||||||
    user = new BehaviorSubject<User>(null);
 | 
						user = new BehaviorSubject<User | false>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private afAuth: AngularFireAuth, private router: Router, private db: AngularFirestore) {
 | 
						private get auth() { return getAuth(); }
 | 
				
			||||||
        this.user.subscribe(user => {
 | 
						private get db() { return getFirestore(); }
 | 
				
			||||||
            this.authenticated = user instanceof Object
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        this.afAuth.user.pipe(
 | 
					 | 
				
			||||||
            flatMap((user: any) => {
 | 
					 | 
				
			||||||
                if(!user) return from([false]);
 | 
					 | 
				
			||||||
                let ref = this.db.collection(this.collection).doc(user.uid);
 | 
					 | 
				
			||||||
                return ref.valueChanges().pipe(map(dbUser => Object.assign({ref: ref}, user, dbUser)))
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        ).subscribe(user => this.user.next(<User>user));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async loginWithGoogle() {
 | 
						constructor(private router: Router) {
 | 
				
			||||||
        this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider());
 | 
							this.user.subscribe(user => this.authenticated = user instanceof Object);
 | 
				
			||||||
        return this.user.pipe(skip(1));
 | 
							this.whoAmI();
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async loginWithFacebook() {
 | 
						async loginWithGoogle() {
 | 
				
			||||||
        this.afAuth.auth.signInWithPopup(new auth.FacebookAuthProvider());
 | 
							const result = await signInWithPopup(this.auth, new GoogleAuthProvider());
 | 
				
			||||||
        return this.user.pipe(skip(1));
 | 
							this.user.next(result.user);
 | 
				
			||||||
    }
 | 
							return result.user;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async logout() {
 | 
						async loginWithFacebook() {
 | 
				
			||||||
        await this.afAuth.auth.signOut();
 | 
							const result = await signInWithPopup(this.auth, new FacebookAuthProvider());
 | 
				
			||||||
        return this.router.navigate(['/']);
 | 
							this.user.next(result.user);
 | 
				
			||||||
    }
 | 
							return result.user;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async logout() {
 | 
				
			||||||
 | 
							await this.auth.signOut();
 | 
				
			||||||
 | 
							this.user.next(false);
 | 
				
			||||||
 | 
							return this.router.navigate(['/']);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async whoAmI() {
 | 
				
			||||||
 | 
							await this.auth.authStateReady();
 | 
				
			||||||
 | 
							const user = this.auth.currentUser || false;
 | 
				
			||||||
 | 
							if(!!user) {
 | 
				
			||||||
 | 
								const ref = doc(collection(this.db, this.collection), user.uid);
 | 
				
			||||||
 | 
								const data = await getDoc(ref);
 | 
				
			||||||
 | 
								Object.assign(user, {ref, ...data});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.user.next(user);
 | 
				
			||||||
 | 
							return user;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,31 @@
 | 
				
			|||||||
import {BehaviorSubject} from "rxjs";
 | 
					import {BehaviorSubject} from "rxjs";
 | 
				
			||||||
import {latLngDistance} from "../utils";
 | 
					import {latLngDistance} from "../utils/misc";
 | 
				
			||||||
import {environment} from "../../environments/environment";
 | 
					import {environment} from "../../environments/environment";
 | 
				
			||||||
import {Circle, LatLng, MapSymbol, Marker, Measurement, Polygon, Polyline, Rectangle} from "../models/mapSymbol";
 | 
					import {Circle, LatLng, MapSymbol, Marker, Measurement, Polygon, Polyline, Rectangle} from "../models/mapSymbol";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare const L;
 | 
					declare const L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum MapLayers {
 | 
					export enum MapLayers {
 | 
				
			||||||
    BING,
 | 
						BING,
 | 
				
			||||||
    GOOGLE_HYBRID,
 | 
						GOOGLE_HYBRID,
 | 
				
			||||||
    GOOGLE_ROAD,
 | 
						GOOGLE_ROAD,
 | 
				
			||||||
    GOOGLE_SATELLITE,
 | 
						GOOGLE_SATELLITE,
 | 
				
			||||||
    GOOGLE_TERRAIN,
 | 
						GOOGLE_TERRAIN,
 | 
				
			||||||
    ESRI_TOPOGRAPHIC,
 | 
						ESRI_TOPOGRAPHIC,
 | 
				
			||||||
    ESRI_IMAGERY,
 | 
						ESRI_IMAGERY,
 | 
				
			||||||
    ESRI_IMAGERY_CLARITY
 | 
						ESRI_IMAGERY_CLARITY
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum WeatherLayers {
 | 
					export enum WeatherLayers {
 | 
				
			||||||
    CLOUDS_NEW,
 | 
						CLOUDS_NEW,
 | 
				
			||||||
    PRECIPITATION_NEW,
 | 
						PRECIPITATION_NEW,
 | 
				
			||||||
    SEA_LEVEL_PRESSURE,
 | 
						SEA_LEVEL_PRESSURE,
 | 
				
			||||||
    WIND_NEW,
 | 
						WIND_NEW,
 | 
				
			||||||
    TEMP_NEW
 | 
						TEMP_NEW
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function buildMarker(color?: string) {
 | 
					function buildMarker(color?: string) {
 | 
				
			||||||
    const markerHtmlStyles = `
 | 
						const markerHtmlStyles = `
 | 
				
			||||||
      background-color: ${color || '#e9403d'};
 | 
					      background-color: ${color || '#e9403d'};
 | 
				
			||||||
      width: 3rem;
 | 
					      width: 3rem;
 | 
				
			||||||
      height: 3rem;
 | 
					      height: 3rem;
 | 
				
			||||||
@@ -36,193 +36,191 @@ function buildMarker(color?: string) {
 | 
				
			|||||||
      border-radius: 3rem 3rem 0;
 | 
					      border-radius: 3rem 3rem 0;
 | 
				
			||||||
      transform: rotate(45deg);
 | 
					      transform: rotate(45deg);
 | 
				
			||||||
      border: 2px solid #FFFFFF`;
 | 
					      border: 2px solid #FFFFFF`;
 | 
				
			||||||
    return L.divIcon({className: "my-custom-pin", iconAnchor: [0, 24], labelAnchor: [-6, 0], popupAnchor: [0, -36], html: `<span style="${markerHtmlStyles}" />`});
 | 
						return L.divIcon({className: "my-custom-pin", iconAnchor: [0, 24], labelAnchor: [-6, 0], popupAnchor: [0, -36], html: `<span style="${markerHtmlStyles}" />`});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ARROW = L.icon({iconUrl: '/assets/images/arrow.png', iconSize: [40, 45], iconAnchor: [20, 23]});
 | 
					const ARROW = L.icon({iconUrl: '/assets/images/arrow.png', iconSize: [40, 45], iconAnchor: [20, 23]});
 | 
				
			||||||
const DOT = L.icon({iconUrl: '/assets/images/dot.png', iconSize: [25, 25], iconAnchor: [13, 13]});
 | 
					const DOT = L.icon({iconUrl: '/assets/images/dot.png', iconSize: [25, 25], iconAnchor: [13, 13]});
 | 
				
			||||||
const MARKER = buildMarker();
 | 
					const MARKER = buildMarker();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class MapService {
 | 
					export class MapService {
 | 
				
			||||||
    private readonly map;
 | 
						private readonly map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private circles = [];
 | 
						private circles: L.Circle[] = [];
 | 
				
			||||||
    private markers = [];
 | 
						private markers: L.Marker[] = [];
 | 
				
			||||||
    private measurements = [];
 | 
						private measurements: any[] = [];
 | 
				
			||||||
    private mapLayer;
 | 
						private mapLayer!: any;
 | 
				
			||||||
    private polygons = [];
 | 
						private polygons: L.Polygon[] = [];
 | 
				
			||||||
    private polylines = [];
 | 
						private polylines: L.Polyline[] = [];
 | 
				
			||||||
    private rectangles = [];
 | 
						private rectangles: L.Rectangle[] = [];
 | 
				
			||||||
    private weatherLayer;
 | 
						private weatherLayer?: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    click = new BehaviorSubject<{latlng: LatLng, symbol?: MapSymbol, item?: any}>(null);
 | 
						click = new BehaviorSubject<{latlng: LatLng, symbol?: MapSymbol, item?: any} | null>(null);
 | 
				
			||||||
    touch = new BehaviorSubject<{type: string, latlng: LatLng}>(null);
 | 
						touch = new BehaviorSubject<{type: string, latlng: LatLng} | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private elementId: string) {
 | 
						constructor(private elementId: string) {
 | 
				
			||||||
        this.map = L.map(elementId, {attributionControl: false, editable: true, tap: true, zoomControl: false, maxBoundsViscosity: 1, doubleClickZoom: false}).setView({lat: 0, lng: 0}, 10);
 | 
							this.map = L.map(this.elementId, {attributionControl: false, editable: true, tap: true, zoomControl: false, maxBoundsViscosity: 1, doubleClickZoom: false}).setView({lat: 0, lng: 0}, 2);
 | 
				
			||||||
        this.map.on('click', (e) => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
							this.map.on('click', (e) => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
				
			||||||
        this.map.on('touchstart', (e) => this.touch.next({type: 'start', latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
							this.map.on('mousedown touchstart', (e) => this.touch.next({type: 'start', latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
				
			||||||
        this.map.on('touchmove', (e) => this.touch.next({type: 'move', latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
							this.map.on('mousemove touchmove', (e) => this.touch.next({type: 'move', latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
				
			||||||
        this.map.on('touchend', (e) => this.touch.next({type: 'end', latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
							this.map.on('mouseup touchend', (e) => this.touch.next({type: 'end', latlng: {lat: e.latlng.lat, lng: e.latlng.lng}}));
 | 
				
			||||||
        this.setMapLayer();
 | 
							this.setMapLayer();
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private getIcon(name: string) {
 | 
						private getIcon(name: string) {
 | 
				
			||||||
        switch(name) {
 | 
							switch(name) {
 | 
				
			||||||
            case 'arrow':
 | 
								case 'arrow':
 | 
				
			||||||
                return ARROW;
 | 
									return ARROW;
 | 
				
			||||||
            case 'dot':
 | 
								case 'dot':
 | 
				
			||||||
                return DOT;
 | 
									return DOT;
 | 
				
			||||||
            default:
 | 
								default:
 | 
				
			||||||
                return MARKER;
 | 
									return MARKER;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    centerOn(latlng: LatLng, zoom=14) {
 | 
						centerOn(latlng: LatLng, zoom=14) {
 | 
				
			||||||
        this.map.setView(latlng, zoom);
 | 
							this.map.setView(latlng, zoom);
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    delete(...symbols) {
 | 
						delete(...symbols) {
 | 
				
			||||||
        symbols.forEach(s => {
 | 
							symbols.forEach(s => {
 | 
				
			||||||
            this.map.removeLayer(s);
 | 
								this.map.removeLayer(s);
 | 
				
			||||||
            this.circles = this.circles.filter(c => c != s);
 | 
								this.circles = this.circles.filter(c => c != s);
 | 
				
			||||||
            this.markers = this.markers.filter(m => m != s);
 | 
								this.markers = this.markers.filter(m => m != s);
 | 
				
			||||||
            this.measurements = this.measurements.filter(m => m != s);
 | 
								this.measurements = this.measurements.filter(m => m != s);
 | 
				
			||||||
            this.polygons = this.polygons.filter(p => p != s);
 | 
								this.polygons = this.polygons.filter(p => p != s);
 | 
				
			||||||
            this.polylines = this.polylines.filter(p => p != s);
 | 
								this.polylines = this.polylines.filter(p => p != s);
 | 
				
			||||||
            this.rectangles = this.rectangles.filter(r => r != s);
 | 
								this.rectangles = this.rectangles.filter(r => r != s);
 | 
				
			||||||
        });
 | 
							});
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    deleteAll() {
 | 
						deleteAll() {
 | 
				
			||||||
        this.circles.forEach(c => this.delete(c));
 | 
							this.circles.forEach(c => this.delete(c));
 | 
				
			||||||
        this.markers.forEach(m => this.delete(m));
 | 
							this.markers.forEach(m => this.delete(m));
 | 
				
			||||||
        this.measurements.forEach(m => this.delete(m));
 | 
							this.measurements.forEach(m => this.delete(m));
 | 
				
			||||||
        this.polygons.forEach(p => this.delete(p));
 | 
							this.polygons.forEach(p => this.delete(p));
 | 
				
			||||||
        this.polylines.forEach(p => this.delete(p));
 | 
							this.polylines.forEach(p => this.delete(p));
 | 
				
			||||||
        this.rectangles.forEach(r => this.delete(r));
 | 
							this.rectangles.forEach(r => this.delete(r));
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    lock(unlock?: boolean) {
 | 
						lock(unlock?: boolean) {
 | 
				
			||||||
        if(unlock) {
 | 
							if(unlock) {
 | 
				
			||||||
            this.map.setMaxBounds(null);
 | 
								this.map.setMaxBounds(null);
 | 
				
			||||||
            this.map.boxZoom.disable();
 | 
								this.map.boxZoom.disable();
 | 
				
			||||||
            this.map.touchZoom.enable();
 | 
								this.map.touchZoom.enable();
 | 
				
			||||||
            this.map.scrollWheelZoom.enable();
 | 
								this.map.scrollWheelZoom.enable();
 | 
				
			||||||
        } else {
 | 
							} else {
 | 
				
			||||||
            this.map.setMaxBounds(this.map.getBounds());
 | 
								this.map.setMaxBounds(this.map.getBounds());
 | 
				
			||||||
            this.map.boxZoom.disable();
 | 
								this.map.boxZoom.disable();
 | 
				
			||||||
            this.map.touchZoom.disable();
 | 
								this.map.touchZoom.disable();
 | 
				
			||||||
            this.map.scrollWheelZoom.disable();
 | 
								this.map.scrollWheelZoom.disable();
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newCircle(c: Circle) {
 | 
						newCircle(c: Circle) {
 | 
				
			||||||
        let circle = L.circle(c.latlng, Object.assign({color: '#e9403d', autoPan: false}, c)).addTo(this.map);
 | 
							const circle = L.circle(c.latlng, Object.assign({color: '#e9403d', autoPan: false}, c)).addTo(this.map);
 | 
				
			||||||
        if(c.label) circle.bindTooltip(c.label, {permanent: true, direction: 'center'});
 | 
							if(c.label) circle.bindTooltip(c.label, {permanent: true, direction: 'center'});
 | 
				
			||||||
        circle.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: c, item: circle}));
 | 
							circle.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: c, item: circle}));
 | 
				
			||||||
        if(!c.noDelete) this.circles.push(circle);
 | 
							if(!c.noDelete) this.circles.push(circle);
 | 
				
			||||||
        return circle;
 | 
							return circle;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newMarker(m: Marker) {
 | 
						newMarker(m: Marker) {
 | 
				
			||||||
        let icon = m.icon ? this.getIcon(m.icon) : buildMarker(m.color);
 | 
							let icon = m.icon ? this.getIcon(m.icon) : buildMarker(m.color);
 | 
				
			||||||
        let marker = L.marker(m.latlng, Object.assign({autoPan: false}, m, {icon: icon})).addTo(this.map);
 | 
							let marker = L.marker(m.latlng, Object.assign({autoPan: false}, m, {icon: icon})).addTo(this.map);
 | 
				
			||||||
        if(m.label) marker.bindTooltip(m.label, {permanent: true, direction: 'bottom'});
 | 
							if(m.label) marker.bindTooltip(m.label, {permanent: true, direction: 'bottom'});
 | 
				
			||||||
        marker.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: marker}));
 | 
							marker.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: marker}));
 | 
				
			||||||
        if(!m.noDelete) this.markers.push(marker);
 | 
							if(!m.noDelete) this.markers.push(marker);
 | 
				
			||||||
        return marker;
 | 
							return marker;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newMeasurement(m: Measurement) {
 | 
						newMeasurement(m: Measurement) {
 | 
				
			||||||
        let line = L.polyline([m.latlng, m.latlng2], Object.assign({color: '#e9403d', autoPan: false, weight: 10, lineCap: "square", dashArray: '10, 20'}, m)).addTo(this.map);
 | 
							let line = L.polyline([m.latlng, m.latlng2], Object.assign({color: '#e9403d', autoPan: false, weight: 10, lineCap: "square", dashArray: '10, 20'}, m)).addTo(this.map);
 | 
				
			||||||
        if(!m.noDelete) this.measurements.push(line);
 | 
							if(!m.noDelete) this.measurements.push(line);
 | 
				
			||||||
        let distance = latLngDistance(m.latlng, m.latlng2);
 | 
							let distance = latLngDistance(m.latlng, m.latlng2);
 | 
				
			||||||
        line.bindPopup(`${distance > 1000 ? Math.round(distance / 100) / 10 : Math.round(distance)} ${distance > 1000 ? 'k' : ''}m`, {autoPan: false, autoClose: false, closeOnClick: false}).openPopup();
 | 
							line.bindPopup(`${distance > 1000 ? Math.round(distance / 100) / 10 : Math.round(distance)} ${distance > 1000 ? 'k' : ''}m`, {autoPan: false, autoClose: false, closeOnClick: false}).openPopup();
 | 
				
			||||||
        line.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: line}));
 | 
							line.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: line}));
 | 
				
			||||||
        return line;
 | 
							return line;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newPolygon(p: Polygon) {
 | 
						newPolygon(p: Polygon) {
 | 
				
			||||||
        let polygon = new L.Polygon(p.latlng, Object.assign({color: '#e9403d', autoPan: false}, p)).addTo(this.map);
 | 
							let polygon = new L.Polygon(p.latlng, Object.assign({color: '#e9403d', autoPan: false}, p)).addTo(this.map);
 | 
				
			||||||
        if(p.label) polygon.bindTooltip(p.label, {permanent: true});
 | 
							if(p.label) polygon.bindTooltip(p.label, {permanent: true});
 | 
				
			||||||
        polygon.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polygon}));
 | 
							polygon.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polygon}));
 | 
				
			||||||
        if(!p.noDelete) this.polygons.push(polygon);
 | 
							if(!p.noDelete) this.polygons.push(polygon);
 | 
				
			||||||
        return polygon;
 | 
							return polygon;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newPolyline(p: Polyline) {
 | 
						newPolyline(p: Polyline) {
 | 
				
			||||||
        let polyline = new L.Polyline(p.latlng, Object.assign({color: '#e9403d', autoPan: false,  weight: 10}, p)).addTo(this.map);
 | 
							let polyline = new L.Polyline(p.latlng, Object.assign({color: '#e9403d', autoPan: false,  weight: 10}, p)).addTo(this.map);
 | 
				
			||||||
        polyline.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polyline}));
 | 
							polyline.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polyline}));
 | 
				
			||||||
        if(!p.noDelete) this.polylines.push(polyline);
 | 
							if(!p.noDelete) this.polylines.push(polyline);
 | 
				
			||||||
        return polyline;
 | 
							return polyline;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newRectangle(r: Rectangle) {
 | 
						newRectangle(r: Rectangle) {
 | 
				
			||||||
        let rect = new L.Rectangle([r.latlng, r.latlng2], Object.assign({color: '#e9403d', autoPan: false}, r)).addTo(this.map);
 | 
							let rect = new L.Rectangle([r.latlng, r.latlng2], Object.assign({color: '#e9403d', autoPan: false}, r)).addTo(this.map);
 | 
				
			||||||
        if(r.label) rect.bindTooltip(r.label, {permanent: true, direction: 'center'});
 | 
							if(r.label) rect.bindTooltip(r.label, {permanent: true, direction: 'center'});
 | 
				
			||||||
        rect.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: r, item: rect}));
 | 
							rect.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: r, item: rect}));
 | 
				
			||||||
        if(!r.noDelete) this.rectangles.push(rect);
 | 
							if(!r.noDelete) this.rectangles.push(rect);
 | 
				
			||||||
        return rect;
 | 
							return rect;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setMapLayer(layer?: MapLayers) {
 | 
						setMapLayer(layer?: MapLayers) {
 | 
				
			||||||
        if(this.mapLayer) this.map.removeLayer(this.mapLayer);
 | 
							if(this.mapLayer) this.map.removeLayer(this.mapLayer);
 | 
				
			||||||
        if(layer == null) layer = MapLayers.GOOGLE_HYBRID;
 | 
							if(layer == null) layer = MapLayers.GOOGLE_HYBRID;
 | 
				
			||||||
        switch(layer) {
 | 
							switch(layer) {
 | 
				
			||||||
            case MapLayers.BING:
 | 
								case MapLayers.BING:
 | 
				
			||||||
                this.mapLayer = L.tileLayer.bing(environment.bing);
 | 
									this.mapLayer = L.tileLayer.bing(environment.bing);
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.GOOGLE_HYBRID:
 | 
								case MapLayers.GOOGLE_HYBRID:
 | 
				
			||||||
                this.mapLayer = L.gridLayer.googleMutant({type: 'hybrid'});
 | 
									this.mapLayer = L.gridLayer.googleMutant({type: 'hybrid'});
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.GOOGLE_ROAD:
 | 
								case MapLayers.GOOGLE_ROAD:
 | 
				
			||||||
                this.mapLayer = L.gridLayer.googleMutant({type: 'roadmap'});
 | 
									this.mapLayer = L.gridLayer.googleMutant({type: 'roadmap'});
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.GOOGLE_SATELLITE:
 | 
								case MapLayers.GOOGLE_SATELLITE:
 | 
				
			||||||
                this.mapLayer = L.gridLayer.googleMutant({type: 'satellite'});
 | 
									this.mapLayer = L.gridLayer.googleMutant({type: 'satellite'});
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.GOOGLE_TERRAIN:
 | 
								case MapLayers.GOOGLE_TERRAIN:
 | 
				
			||||||
                this.mapLayer = L.gridLayer.googleMutant({type: 'terrain'});
 | 
									this.mapLayer = L.gridLayer.googleMutant({type: 'terrain'});
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.ESRI_TOPOGRAPHIC:
 | 
								case MapLayers.ESRI_TOPOGRAPHIC:
 | 
				
			||||||
                this.mapLayer = L.esri.basemapLayer('Topographic');
 | 
									this.mapLayer = L.esri.basemapLayer('Topographic');
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.ESRI_IMAGERY:
 | 
								case MapLayers.ESRI_IMAGERY:
 | 
				
			||||||
                this.mapLayer = L.esri.basemapLayer('Imagery');
 | 
									this.mapLayer = L.esri.basemapLayer('Imagery');
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case MapLayers.ESRI_IMAGERY_CLARITY:
 | 
								case MapLayers.ESRI_IMAGERY_CLARITY:
 | 
				
			||||||
                this.mapLayer = L.esri.basemapLayer('ImageryClarity');
 | 
									this.mapLayer = L.esri.basemapLayer('ImageryClarity');
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
        this.mapLayer.addTo(this.map);
 | 
							this.mapLayer.addTo(this.map);
 | 
				
			||||||
        if(this.weatherLayer) this.setWeatherLayer(this.weatherLayer.name);
 | 
							if(this.weatherLayer) this.setWeatherLayer(this.weatherLayer.name);
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setWeatherLayer(layer?: WeatherLayers) {
 | 
						setWeatherLayer(layer?: WeatherLayers) {
 | 
				
			||||||
        if(this.weatherLayer) {
 | 
							if(this.weatherLayer) {
 | 
				
			||||||
            this.map.removeLayer(this.weatherLayer.layer);
 | 
								this.map.removeLayer(this.weatherLayer.layer);
 | 
				
			||||||
            this.weatherLayer = null;
 | 
								this.weatherLayer = null;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
        switch(layer) {
 | 
							switch(layer) {
 | 
				
			||||||
            case WeatherLayers.CLOUDS_NEW:
 | 
								case WeatherLayers.CLOUDS_NEW:
 | 
				
			||||||
                this.weatherLayer = {name: WeatherLayers.CLOUDS_NEW, layer: L.OWM.clouds({appId: environment.openWeather, opacity: 0.5})};
 | 
									this.weatherLayer = {name: WeatherLayers.CLOUDS_NEW, layer: L.OWM.clouds({appId: environment.openWeather, opacity: 0.5})};
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case WeatherLayers.PRECIPITATION_NEW:
 | 
								case WeatherLayers.PRECIPITATION_NEW:
 | 
				
			||||||
                this.weatherLayer = {name: WeatherLayers.PRECIPITATION_NEW, layer: L.OWM.precipitation({appId: environment.openWeather, opacity: 0.5})};
 | 
									this.weatherLayer = {name: WeatherLayers.PRECIPITATION_NEW, layer: L.OWM.precipitation({appId: environment.openWeather, opacity: 0.5})};
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case WeatherLayers.SEA_LEVEL_PRESSURE:
 | 
								case WeatherLayers.SEA_LEVEL_PRESSURE:
 | 
				
			||||||
                this.weatherLayer = {name: WeatherLayers.SEA_LEVEL_PRESSURE, layer: L.OWM.pressure({appId: environment.openWeather, opacity: 0.5})};
 | 
									this.weatherLayer = {name: WeatherLayers.SEA_LEVEL_PRESSURE, layer: L.OWM.pressure({appId: environment.openWeather, opacity: 0.5})};
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case WeatherLayers.WIND_NEW:
 | 
								case WeatherLayers.WIND_NEW:
 | 
				
			||||||
                this.weatherLayer = {name: WeatherLayers.WIND_NEW, layer: L.OWM.wind({appId: environment.openWeather, opacity: 0.5})};
 | 
									this.weatherLayer = {name: WeatherLayers.WIND_NEW, layer: L.OWM.wind({appId: environment.openWeather, opacity: 0.5})};
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
            case WeatherLayers.TEMP_NEW:
 | 
								case WeatherLayers.TEMP_NEW:
 | 
				
			||||||
                this.weatherLayer = {name: WeatherLayers.TEMP_NEW, layer: L.OWM.temperature({appId: environment.openWeather, opacity: 0.5})};
 | 
									this.weatherLayer = {name: WeatherLayers.TEMP_NEW, layer: L.OWM.temperature({appId: environment.openWeather, opacity: 0.5})};
 | 
				
			||||||
                break;
 | 
									break;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
        if(this.weatherLayer) this.weatherLayer.layer.addTo(this.map);
 | 
							if(this.weatherLayer) this.weatherLayer.layer.addTo(this.map);
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,65 +1,66 @@
 | 
				
			|||||||
import {EventEmitter, Injectable} from '@angular/core';
 | 
					import {EventEmitter, Injectable} from '@angular/core';
 | 
				
			||||||
import {BehaviorSubject, combineLatest} from "rxjs";
 | 
					import {BehaviorSubject, combineLatest} from "rxjs";
 | 
				
			||||||
import {PermissionsService} from "../components/permissions/permissions.service";
 | 
					import {PermissionsService} from "../components/permissions/permissions.service";
 | 
				
			||||||
 | 
					import {Position} from '../models/mapSymbol';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
    providedIn: 'root'
 | 
						providedIn: 'root'
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class PhysicsService {
 | 
					export class PhysicsService {
 | 
				
			||||||
    private _mode: string;
 | 
						private _mode!: string;
 | 
				
			||||||
    get mode() { return this._mode; }
 | 
						get mode() { return this._mode; }
 | 
				
			||||||
    set mode(mode: string) {
 | 
						set mode(mode: string) {
 | 
				
			||||||
        this._mode = mode;
 | 
							this._mode = mode;
 | 
				
			||||||
        localStorage.setItem('headingMode', mode);
 | 
							localStorage.setItem('headingMode', mode);
 | 
				
			||||||
        this.calibrate.next(this.calibrate.value);
 | 
							this.calibrate.next(this.calibrate.value);
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    requireCalibration = new EventEmitter();
 | 
						requireCalibration = new EventEmitter();
 | 
				
			||||||
    calibrate = new BehaviorSubject<number>(Infinity);
 | 
						calibrate = new BehaviorSubject<number>(Infinity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info = new BehaviorSubject(null);
 | 
						info = new BehaviorSubject<any>(null);
 | 
				
			||||||
    motion = new BehaviorSubject<DeviceMotionEvent>(null);
 | 
						motion = new BehaviorSubject<DeviceMotionEvent | null>(null);
 | 
				
			||||||
    orientation = new BehaviorSubject<DeviceOrientationEvent>(null);
 | 
						orientation = new BehaviorSubject<DeviceOrientationEvent | null>(null);
 | 
				
			||||||
    position = new BehaviorSubject<Position>(null);
 | 
						position = new BehaviorSubject<Position | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(permissionsService: PermissionsService) {
 | 
						constructor(permissionsService: PermissionsService) {
 | 
				
			||||||
        this.mode = localStorage.getItem('headingMode');
 | 
							this.mode = localStorage.getItem('headingMode');
 | 
				
			||||||
        permissionsService.requestPermission('geolocation', 'gps_fixed', 'Can we use your location?').then(granted => {
 | 
							permissionsService.requestPermission('geolocation', 'gps_fixed', 'Can we use your location?').then(granted => {
 | 
				
			||||||
            if(granted) {
 | 
								if(granted) {
 | 
				
			||||||
                // Gather physical data
 | 
									// Gather physical data
 | 
				
			||||||
                window.addEventListener('devicemotion', motion => this.motion.next(motion));
 | 
									window.addEventListener('devicemotion', motion => this.motion.next(motion));
 | 
				
			||||||
                window.addEventListener('deviceorientation', orientation => this.orientation.next(orientation));
 | 
									window.addEventListener('deviceorientation', orientation => this.orientation.next(orientation));
 | 
				
			||||||
                navigator.geolocation.watchPosition(position => this.position.next(position));
 | 
									navigator.geolocation.watchPosition(position => this.position.next(<any>position));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Combine data into one nice package
 | 
									// Combine data into one nice package
 | 
				
			||||||
                combineLatest(this.position, this.orientation, this.calibrate).subscribe(data => {
 | 
									combineLatest(this.position, this.orientation, this.calibrate).subscribe((data: any) => {
 | 
				
			||||||
                    if(!data[0]) return;
 | 
										if(!data[0]) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let info = {
 | 
										let info = {
 | 
				
			||||||
                        accuracy: data[0].coords.accuracy,
 | 
											accuracy: data[0].coords.accuracy,
 | 
				
			||||||
                        altitude: data[0].coords.altitude,
 | 
											altitude: data[0].coords.altitude,
 | 
				
			||||||
                        altitudeAccuracy: data[0].coords.altitudeAccuracy,
 | 
											altitudeAccuracy: data[0].coords.altitudeAccuracy,
 | 
				
			||||||
                        heading: data[0].coords.heading,
 | 
											heading: data[0].coords.heading,
 | 
				
			||||||
                        latitude: data[0].coords.latitude,
 | 
											latitude: data[0].coords.latitude,
 | 
				
			||||||
                        longitude: data[0].coords.longitude,
 | 
											longitude: data[0].coords.longitude,
 | 
				
			||||||
                        speed: data[0].coords.speed
 | 
											speed: data[0].coords.speed
 | 
				
			||||||
                    };
 | 
										};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if(this.mode == null) this.mode = info.heading ? 'gps' : 'orientation';
 | 
										if(this.mode == null) this.mode = info.heading ? 'gps' : 'orientation';
 | 
				
			||||||
                    if(this.mode == 'orientation') {
 | 
										if(this.mode == 'orientation') {
 | 
				
			||||||
                        if((!data[1] || !data[1].absolute) && data[2] == Infinity) {
 | 
											if((!data[1] || !data[1].absolute) && data[2] == Infinity) {
 | 
				
			||||||
                            this.calibrate.next(0);
 | 
												this.calibrate.next(0);
 | 
				
			||||||
                            this.requireCalibration.emit();
 | 
												this.requireCalibration.emit();
 | 
				
			||||||
                        }
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        info.heading = -(data[1] ? data[1].alpha : 0) + (data[2] == Infinity ? 0 : data[2]);
 | 
											info.heading = -Number(data[1] ? data[1].alpha : 0) + Number(data[2] == Infinity ? 0 : data[2]);
 | 
				
			||||||
                        if(info.heading < 0) info.heading += 360;
 | 
											if(info.heading < 0) info.heading += 360;
 | 
				
			||||||
                        if(info.heading >= 360) info.heading -= 360;
 | 
											if(info.heading >= 360) info.heading -= 360;
 | 
				
			||||||
                    }
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    this.info.next(info);
 | 
										this.info.next(info);
 | 
				
			||||||
                })
 | 
									})
 | 
				
			||||||
            }
 | 
								}
 | 
				
			||||||
        });
 | 
							});
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,201 +1,200 @@
 | 
				
			|||||||
import {Injectable} from "@angular/core";
 | 
					import {Injectable} from "@angular/core";
 | 
				
			||||||
import {AngularFirestore, AngularFirestoreDocument} from "@angular/fire/firestore";
 | 
					import {onSnapshot, setDoc, collection, getFirestore, doc, DocumentReference, getDoc} from 'firebase/firestore';
 | 
				
			||||||
import {BehaviorSubject, combineLatest, Subscription} from "rxjs";
 | 
					import {BehaviorSubject, Subscription} from "rxjs";
 | 
				
			||||||
import {Circle, MapData, MapSymbol, Marker, Measurement, Polygon, Polyline, Position, Rectangle} from "../models/mapSymbol";
 | 
					import {Circle, MapData, MapSymbol, Marker, Measurement, Polygon, Polyline, Position, Rectangle} from "../models/mapSymbol";
 | 
				
			||||||
import {Md5} from 'ts-md5';
 | 
					import {filter} from "rxjs/operators";
 | 
				
			||||||
import {filter, map} from "rxjs/operators";
 | 
					import {randomStringBuilder} from '../utils/string';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const LOCATION_COLLECTION = 'Users';
 | 
					export const LOCATION_COLLECTION = 'Users';
 | 
				
			||||||
export const MAP_COLLECTION = 'Maps';
 | 
					export const MAP_COLLECTION = 'Maps';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
    providedIn: 'root'
 | 
						providedIn: 'root'
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class SyncService {
 | 
					export class SyncService {
 | 
				
			||||||
    private location;
 | 
						private location?: any;
 | 
				
			||||||
    private locationChanged = false;
 | 
						private locationChanged = false;
 | 
				
			||||||
    private locationDoc: AngularFirestoreDocument;
 | 
						private locationDoc?: DocumentReference | null;
 | 
				
			||||||
    private mapCode: string;
 | 
						private locationSub?: Function;
 | 
				
			||||||
    private mapDoc: AngularFirestoreDocument;
 | 
						private mapCode?: string | null;
 | 
				
			||||||
    private mapChanged = false;
 | 
						private mapDoc?: DocumentReference | null;
 | 
				
			||||||
    private mapSub: Subscription;
 | 
						private mapChanged = false;
 | 
				
			||||||
    private saveInterval: number;
 | 
						private mapSub?: Subscription | null;
 | 
				
			||||||
    private username: string;
 | 
						private saveInterval?: any;
 | 
				
			||||||
 | 
						private username?: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    freeze = new BehaviorSubject<boolean>(false);
 | 
						freeze = new BehaviorSubject<boolean>(false);
 | 
				
			||||||
    mapData = new BehaviorSubject<MapData>({});
 | 
						mapData = new BehaviorSubject<MapData>({});
 | 
				
			||||||
    status = new BehaviorSubject<string>(null);
 | 
						status = new BehaviorSubject<string | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private db: AngularFirestore) {
 | 
						get db() { return getFirestore(); }
 | 
				
			||||||
        // Handle prompting the user before exit if there are changes
 | 
					 | 
				
			||||||
        this.status.pipe(filter(s => !s)).subscribe(() => window.onbeforeunload = () => this.unload());
 | 
					 | 
				
			||||||
        this.status.pipe(filter(s => !!s)).subscribe(() => {
 | 
					 | 
				
			||||||
            window.onbeforeunload = e => {
 | 
					 | 
				
			||||||
                this.removeLocation();
 | 
					 | 
				
			||||||
                let ignore = this.save();
 | 
					 | 
				
			||||||
                e.returnValue = 'Please wait for us to finish saving!';
 | 
					 | 
				
			||||||
                return e.returnValue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private addMapSymbol(s: MapSymbol, key: string) {
 | 
						constructor() {
 | 
				
			||||||
        let map = this.mapData.value;
 | 
							// Handle prompting the user before exit if there are changes
 | 
				
			||||||
        if(!map[key]) map[key] = {};
 | 
							this.status.pipe(filter(s => !s)).subscribe(() => window.onbeforeunload = () => this.unload());
 | 
				
			||||||
        s.updated = new Date().getTime();
 | 
							this.status.pipe(filter(s => !!s)).subscribe(() => {
 | 
				
			||||||
        if(!s.id) s.id = Md5.hashStr(s.updated.toString()).toString();
 | 
								window.onbeforeunload = e => {
 | 
				
			||||||
        map[key][s.id] = s;
 | 
									this.removeLocation();
 | 
				
			||||||
        this.mapData.next(map);
 | 
									let ignore = this.save();
 | 
				
			||||||
        this.mapChanged = true;
 | 
									e.returnValue = 'Please wait for us to finish saving!';
 | 
				
			||||||
        this.status.next('modified');
 | 
									return e.returnValue;
 | 
				
			||||||
    }
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async exists(mapCode: string) {
 | 
						private addMapSymbol(s: MapSymbol, key: string) {
 | 
				
			||||||
        return (await this.db.collection(MAP_COLLECTION).doc(mapCode).ref.get()).exists;
 | 
							let map = this.mapData.value;
 | 
				
			||||||
    }
 | 
							if(!map[key]) map[key] = {};
 | 
				
			||||||
 | 
							s.updated = new Date().getTime();
 | 
				
			||||||
 | 
							if(!s.id) s.id = randomStringBuilder(32, true, true);
 | 
				
			||||||
 | 
							map[key][s.id] = s;
 | 
				
			||||||
 | 
							this.mapData.next(map);
 | 
				
			||||||
 | 
							this.mapChanged = true;
 | 
				
			||||||
 | 
							this.status.next('modified');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addCircle(circle: Circle) {
 | 
						async exists(mapCode: string) {
 | 
				
			||||||
        this.addMapSymbol(circle, 'circles');
 | 
							const value = await getDoc(doc(collection(this.db, MAP_COLLECTION), mapCode));
 | 
				
			||||||
    }
 | 
							return value.exists();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addMarker(marker: Marker) {
 | 
						addCircle(circle: Circle) {
 | 
				
			||||||
        this.addMapSymbol(marker, 'markers');
 | 
							this.addMapSymbol(circle, 'circles');
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addMeasurement(measurement: Measurement) {
 | 
						addMarker(marker: Marker) {
 | 
				
			||||||
        this.addMapSymbol(measurement, 'measurements');
 | 
							this.addMapSymbol(marker, 'markers');
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addMyLocation(location: Position) {
 | 
						addMeasurement(measurement: Measurement) {
 | 
				
			||||||
        location.timestamp = new Date();
 | 
							this.addMapSymbol(measurement, 'measurements');
 | 
				
			||||||
        let markForSave = this.location == null;
 | 
						}
 | 
				
			||||||
        this.locationChanged = true;
 | 
					 | 
				
			||||||
        this.location = location;
 | 
					 | 
				
			||||||
        if(markForSave) return this.save(false, true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addPolygon(polygon: Polygon) {
 | 
						async addMyLocation(location: Position) {
 | 
				
			||||||
        this.addMapSymbol(polygon, 'polygons');
 | 
							location.timestamp = new Date();
 | 
				
			||||||
    }
 | 
							let markForSave = this.location == null;
 | 
				
			||||||
 | 
							this.locationChanged = true;
 | 
				
			||||||
 | 
							this.location = location;
 | 
				
			||||||
 | 
							if(markForSave)
 | 
				
			||||||
 | 
								await this.save(false, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addPolyline(polyline: Polyline) {
 | 
						addPolygon(polygon: Polygon) {
 | 
				
			||||||
        this.addMapSymbol(polyline, 'polylines')
 | 
							this.addMapSymbol(polygon, 'polygons');
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addRectangle(rect: Rectangle) {
 | 
						addPolyline(polyline: Polyline) {
 | 
				
			||||||
        this.addMapSymbol(rect, 'rectangles')
 | 
							this.addMapSymbol(polyline, 'polylines')
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    delete(...symbols) {
 | 
						addRectangle(rect: Rectangle) {
 | 
				
			||||||
        let map = this.mapData.value;
 | 
							this.addMapSymbol(rect, 'rectangles')
 | 
				
			||||||
        Object.keys(map).forEach(key => symbols.filter(s => !!map[key][s.id]).forEach(s => {
 | 
						}
 | 
				
			||||||
            map[key][s.id].updated = new Date().getTime();
 | 
					 | 
				
			||||||
            map[key][s.id].deleted = true
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
        this.mapData.next(map);
 | 
					 | 
				
			||||||
        this.mapChanged = true;
 | 
					 | 
				
			||||||
        this.status.next('modified');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    load(mapCode: string, username: string) {
 | 
						delete(...symbols) {
 | 
				
			||||||
        this.mapCode = mapCode;
 | 
							let map = this.mapData.value;
 | 
				
			||||||
        this.username = username.replace(/\s/g, '');
 | 
							Object.keys(map).forEach(key => symbols.filter(s => !!map[key][s.id]).forEach(s => {
 | 
				
			||||||
        this.mapDoc = this.db.collection(MAP_COLLECTION).doc(mapCode);
 | 
								map[key][s.id].updated = new Date().getTime();
 | 
				
			||||||
        this.locationDoc = this.mapDoc.collection(LOCATION_COLLECTION).doc(username);
 | 
								map[key][s.id].deleted = true
 | 
				
			||||||
 | 
							}));
 | 
				
			||||||
 | 
							this.mapData.next(map);
 | 
				
			||||||
 | 
							this.mapChanged = true;
 | 
				
			||||||
 | 
							this.status.next('modified');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.mapDoc.valueChanges().subscribe(() => this.status.next(null));
 | 
						load(mapCode: string, username: string) {
 | 
				
			||||||
        this.mapSub = combineLatest(this.mapDoc.valueChanges(), this.mapDoc.collection(LOCATION_COLLECTION, ref => {
 | 
							this.mapCode = mapCode;
 | 
				
			||||||
            let aMinuteAgo = new Date();
 | 
							this.username = username.replace(/\s/g, '');
 | 
				
			||||||
            aMinuteAgo.setMinutes(aMinuteAgo.getMinutes() - 1);
 | 
							this.mapDoc = doc(collection(this.db, MAP_COLLECTION), mapCode);
 | 
				
			||||||
            return ref.where('timestamp', '>=', aMinuteAgo);
 | 
							this.locationDoc = doc(collection(this.db, LOCATION_COLLECTION), username);
 | 
				
			||||||
        }).snapshotChanges(), this.freeze)
 | 
					 | 
				
			||||||
            .pipe(map(data => {
 | 
					 | 
				
			||||||
                let oldMap = this.mapData.value;
 | 
					 | 
				
			||||||
                if(data[2]) return;
 | 
					 | 
				
			||||||
                let newMap = data[0] || {};
 | 
					 | 
				
			||||||
                let mergedMap = this.mergeMaps(newMap, oldMap);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let locations = data[1].map(doc => ({id: doc.payload.doc.id, data: <Marker>doc.payload.doc.data()}));
 | 
							onSnapshot(this.mapDoc, map => {
 | 
				
			||||||
                locations.filter(l => l.id != username).forEach(l => {
 | 
								this.status.next(null);
 | 
				
			||||||
                    mergedMap.locations[l.id] = l.data;
 | 
								const data = this.mergeMaps( map.data() || {}, this.mapData.value);
 | 
				
			||||||
                });
 | 
								if(this.locationSub) this.locationSub();
 | 
				
			||||||
 | 
								this.locationSub = onSnapshot(collection(this.mapDoc, LOCATION_COLLECTION), docs => {
 | 
				
			||||||
 | 
									docs.forEach(doc => {
 | 
				
			||||||
 | 
										const d: any = doc.data();
 | 
				
			||||||
 | 
										if(d.timestamp.seconds * 1000 > (new Date().getTime() - 60000 * 3))
 | 
				
			||||||
 | 
											data.locations[doc.id] = <any>{id: doc.id, ...doc.data()}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									this.mapData.next(data);
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								this.mapData.next(data);
 | 
				
			||||||
 | 
								if(this.saveInterval) clearInterval(this.saveInterval);
 | 
				
			||||||
 | 
								const hasUserLocations = Object.keys(this.mapData.value?.locations)?.length > 0 || false;
 | 
				
			||||||
 | 
								this.saveInterval = setInterval(() => this.save(), hasUserLocations ? 5_000 : 30_000);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return mergedMap;
 | 
						}
 | 
				
			||||||
            })).subscribe((mapData: MapData) => {
 | 
					 | 
				
			||||||
                if(!mapData) return;
 | 
					 | 
				
			||||||
                this.mapData.next(mapData);
 | 
					 | 
				
			||||||
                if(this.saveInterval) clearInterval(this.saveInterval);
 | 
					 | 
				
			||||||
                this.saveInterval = setInterval(() => this.save(), (mapData.locations && Object.keys(mapData.locations).length > 0) ? 5_000 : 30_000);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mergeMaps(newMap: MapData, oldMap: MapData) {
 | 
						mergeMaps(newMap: MapData, oldMap: MapData) {
 | 
				
			||||||
        let map: MapData = {locations: {}};
 | 
							let map: MapData = {locations: {}};
 | 
				
			||||||
        let twoMinAgo = new Date();
 | 
							let twoMinAgo = new Date();
 | 
				
			||||||
        twoMinAgo.setMinutes(twoMinAgo.getMinutes() - 2);
 | 
							twoMinAgo.setMinutes(twoMinAgo.getMinutes() - 2);
 | 
				
			||||||
        Object.keys(newMap).forEach(key => {
 | 
							Object.keys(newMap).forEach(key => {
 | 
				
			||||||
            if(!map[key]) map[key] = {};
 | 
								if(!map[key]) map[key] = {};
 | 
				
			||||||
            Object.keys(newMap[key]).filter(id => !newMap[key][id].deleted || newMap[key][id].updated > twoMinAgo)
 | 
								Object.keys(newMap[key]).filter(id => !newMap[key][id].deleted || newMap[key][id].updated > twoMinAgo)
 | 
				
			||||||
                .forEach(id => map[key][id] = newMap[key][id]);
 | 
									.forEach(id => map[key][id] = newMap[key][id]);
 | 
				
			||||||
        });
 | 
							});
 | 
				
			||||||
        Object.keys(oldMap).filter(key => key != 'locations').forEach(key => {
 | 
							Object.keys(oldMap).filter(key => key != 'locations').forEach(key => {
 | 
				
			||||||
            if(!map[key]) map[key] = {};
 | 
								if(!map[key]) map[key] = {};
 | 
				
			||||||
            Object.keys(oldMap[key]).filter(id => {
 | 
								Object.keys(oldMap[key]).filter(id => {
 | 
				
			||||||
                let newS = map[key][id] || false;
 | 
									let newS = map[key][id] || false;
 | 
				
			||||||
                return !newS && !oldMap[key][id].deleted || newS && oldMap[key][id].updated > newS.updated;
 | 
									return !newS && !oldMap[key][id].deleted || newS && oldMap[key][id].updated > newS.updated;
 | 
				
			||||||
            }).forEach(id => map[key][id] = oldMap[key][id]);
 | 
								}).forEach(id => map[key][id] = oldMap[key][id]);
 | 
				
			||||||
        });
 | 
							});
 | 
				
			||||||
        return map;
 | 
							return map;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    removeLocation() {
 | 
						removeLocation() {
 | 
				
			||||||
        // Hack to delete doc even if page is closed
 | 
							// Hack to delete doc even if page is closed
 | 
				
			||||||
        navigator.sendBeacon(`https://us-central1-mapalliance-ab38a.cloudfunctions.net/closeSession/?mapCode=${this.mapCode}&username=${this.username}`);
 | 
							navigator.sendBeacon(`https://us-central1-mapalliance-ab38a.cloudfunctions.net/closeSession/?mapCode=${this.mapCode}&username=${this.username}`);
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    save(map=true, location=true) {
 | 
						save(map=true, location=true) {
 | 
				
			||||||
        let promises = [];
 | 
							let promises: Promise<any>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(location && this.locationDoc && this.locationChanged) {
 | 
							if(location && this.locationDoc && this.locationChanged) {
 | 
				
			||||||
            promises.push(this.locationDoc.set(this.location));
 | 
								promises.push(setDoc(this.locationDoc, this.location));
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(map && this.mapDoc && this.mapChanged) {
 | 
							if(map && this.mapDoc && this.mapChanged) {
 | 
				
			||||||
            this.status.next('saving');
 | 
								this.status.next('saving');
 | 
				
			||||||
            let map = this.mapData.value;
 | 
								let map = this.mapData.value;
 | 
				
			||||||
            delete map.locations;
 | 
								delete map.locations;
 | 
				
			||||||
            promises.push(this.mapDoc.set(map));
 | 
								promises.push(setDoc(this.mapDoc, map));
 | 
				
			||||||
            this.mapChanged = false;
 | 
								this.mapChanged = false;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Promise.all(promises)
 | 
							return Promise.all(promises)
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unload() {
 | 
						unload() {
 | 
				
			||||||
        this.removeLocation();
 | 
							this.removeLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(this.saveInterval) clearInterval(this.saveInterval);
 | 
							if(this.saveInterval) clearInterval(this.saveInterval);
 | 
				
			||||||
        let saving = this.save(true, false);
 | 
							let saving = this.save(true, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(this.mapSub) {
 | 
							if(this.mapSub) {
 | 
				
			||||||
            this.mapSub.unsubscribe();
 | 
								this.mapSub.unsubscribe();
 | 
				
			||||||
            this.mapSub = null;
 | 
								this.mapSub = null;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(this.mapDoc) {
 | 
							if(this.mapDoc) {
 | 
				
			||||||
            this.mapDoc = null;
 | 
								this.mapDoc = null;
 | 
				
			||||||
            this.mapChanged = false;
 | 
								this.mapChanged = false;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(this.locationDoc) {
 | 
							if(this.locationDoc) {
 | 
				
			||||||
            this.location = null;
 | 
								this.location = null;
 | 
				
			||||||
            this.locationChanged = false;
 | 
								this.locationChanged = false;
 | 
				
			||||||
            this.locationDoc = null;
 | 
								this.locationDoc = null;
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.mapCode = null;
 | 
							this.mapCode = null;
 | 
				
			||||||
        this.username = null;
 | 
							this.username = null;
 | 
				
			||||||
        this.mapData.next({});
 | 
							this.mapData.next({});
 | 
				
			||||||
        return saving;
 | 
							return saving;
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import {LatLng} from "./models/mapSymbol";
 | 
					import {LatLng} from "../models/mapSymbol";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const R = 6_371; // Radius of the Earth
 | 
					export const R = 6_371; // Radius of the Earth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										136
									
								
								src/app/utils/string.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/app/utils/string.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * String of all letters
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const LETTER_LIST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * String of all numbers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const NUMBER_LIST = '0123456789';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * String of all symbols
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const SYMBOL_LIST = '~`!@#$%^&*()_-+={[}]|\\:;"\'<,>.?/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * String of all letters, numbers & symbols
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const CHAR_LIST = LETTER_LIST + NUMBER_LIST + SYMBOL_LIST;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function formatPhoneNumber(number: string) {
 | 
				
			||||||
 | 
						const parts = /(\+?1)?.*?(\d{3}).*?(\d{3}).*?(\d{4})/g.exec(number);
 | 
				
			||||||
 | 
						if(!parts) throw new Error(`Number cannot be parsed: ${number}`);
 | 
				
			||||||
 | 
						return `${parts[1] ?? ''} (${parts[2]}) ${parts[3]}-${parts[4]}`.trim();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Insert a string into another string at a given position
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @example
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 * console.log(insertAt('Hello world!', ' glorious', 5);
 | 
				
			||||||
 | 
					 * // Output: Hello glorious world!
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {string} target - Parent string you want to modify
 | 
				
			||||||
 | 
					 * @param {string} str - Value that will be injected to parent
 | 
				
			||||||
 | 
					 * @param {number} index - Position to inject string at
 | 
				
			||||||
 | 
					 * @returns {string} - New string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function insertAt(target: string, str: string, index: number): String {
 | 
				
			||||||
 | 
						return `${target.slice(0, index)}${str}${target.slice(index + 1)}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generate a string of random characters.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @example
 | 
				
			||||||
 | 
					 * ```ts
 | 
				
			||||||
 | 
					 * const random = randomString();
 | 
				
			||||||
 | 
					 * const randomByte = randomString(8, "01")
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {number} length - length of generated string
 | 
				
			||||||
 | 
					 * @param {string} pool - character pool to generate string from
 | 
				
			||||||
 | 
					 * @return {string} generated string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function randomString(length: number, pool: string = CHAR_LIST): string {
 | 
				
			||||||
 | 
						return Array(length).fill(null).map(() => {
 | 
				
			||||||
 | 
							const n = ~~(Math.random() * pool.length);
 | 
				
			||||||
 | 
							return pool[n];
 | 
				
			||||||
 | 
						}).join('');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generate a random string with fine control over letters, numbers & symbols
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @example
 | 
				
			||||||
 | 
					 * ```ts
 | 
				
			||||||
 | 
					 * const randomLetter = randomString(1, true);
 | 
				
			||||||
 | 
					 * const randomChar = randomString(1, true, true, true);
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {number} length - length of generated string
 | 
				
			||||||
 | 
					 * @param {boolean} letters - Add letters to pool
 | 
				
			||||||
 | 
					 * @param {boolean} numbers - Add numbers to pool
 | 
				
			||||||
 | 
					 * @param {boolean} symbols - Add symbols to pool
 | 
				
			||||||
 | 
					 * @return {string} generated string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function randomStringBuilder(length: number, letters = false, numbers = false, symbols = false): string {
 | 
				
			||||||
 | 
						if(!letters && !numbers && !symbols) throw new Error('Must enable at least one: letters, numbers, symbols');
 | 
				
			||||||
 | 
						return Array(length).fill(null).map(() => {
 | 
				
			||||||
 | 
							let c;
 | 
				
			||||||
 | 
							do {
 | 
				
			||||||
 | 
								const type = ~~(Math.random() * 3);
 | 
				
			||||||
 | 
								if(letters && type == 0) {
 | 
				
			||||||
 | 
									c = LETTER_LIST[~~(Math.random() * LETTER_LIST.length)];
 | 
				
			||||||
 | 
								} else if(numbers && type == 1) {
 | 
				
			||||||
 | 
									c = NUMBER_LIST[~~(Math.random() * NUMBER_LIST.length)];
 | 
				
			||||||
 | 
								} else if(symbols && type == 2) {
 | 
				
			||||||
 | 
									c = SYMBOL_LIST[~~(Math.random() * SYMBOL_LIST.length)];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} while(!c);
 | 
				
			||||||
 | 
							return c;
 | 
				
			||||||
 | 
						}).join('');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Find all substrings that match a given pattern.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Roughly based on `String.prototype.matchAll`.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {string} value - String to search.
 | 
				
			||||||
 | 
					 * @param {RegExp | string} regex - Regular expression to match.
 | 
				
			||||||
 | 
					 * @return {RegExpExecArray[]} Found matches.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function matchAll(value: string, regex: RegExp | string): RegExpExecArray[] {
 | 
				
			||||||
 | 
						if(typeof regex === 'string') {
 | 
				
			||||||
 | 
							regex = new RegExp(regex, 'g');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// https://stackoverflow.com/a/60290199
 | 
				
			||||||
 | 
						if(!regex.global) {
 | 
				
			||||||
 | 
							throw new TypeError('Regular expression must be global.');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let ret: RegExpExecArray[] = [];
 | 
				
			||||||
 | 
						let match: RegExpExecArray | null;
 | 
				
			||||||
 | 
						while((match = regex.exec(value)) !== null) {
 | 
				
			||||||
 | 
							ret.push(match);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Check if email is valid
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {string} email - Target
 | 
				
			||||||
 | 
					 * @returns {boolean} - Follows format
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function validateEmail(email: string) {
 | 
				
			||||||
 | 
						return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,42 +1,42 @@
 | 
				
			|||||||
<div [@fadeIn] class="h-100 w-100">
 | 
					<div [@fadeIn] class="h-100 w-100">
 | 
				
			||||||
    <stary-sky></stary-sky>
 | 
					    <stary-sky></stary-sky>
 | 
				
			||||||
    <div class="badge">
 | 
						<div class="controls d-flex flex-column">
 | 
				
			||||||
        <img src="assets/images/logo.png" width="200px" height="auto">
 | 
							<img src="assets/images/logo.png" class="mb-4 py-1" alt="logo" width="200px">
 | 
				
			||||||
    </div>
 | 
							<div *ngIf="!authService.authenticated">
 | 
				
			||||||
    <div class="controls">
 | 
								<!--<button mat-flat-button class="w-100 text-white mb-3 py-2" style="background-color: #3b5998" (click)="authService.loginWithFacebook()">
 | 
				
			||||||
        <div *ngIf="!authService.authenticated">
 | 
									<img class="mr-2" src="/assets/images/facebook.png" height="18px" width="auto"> Facebook
 | 
				
			||||||
            <!--<button mat-flat-button class="w-100 text-white mb-3" style="background-color: #3b5998" (click)="authService.loginWithFacebook()">
 | 
								</button>-->
 | 
				
			||||||
                <img class="mr-2" src="/assets/images/facebook.png" height="18px" width="auto"> Facebook
 | 
								<button mat-flat-button class="w-100 mb-1 py-2" style="background-color: #efe8e8" (click)="authService.loginWithGoogle()">
 | 
				
			||||||
            </button>-->
 | 
									<img class="mr-2" src="/assets/images/google.png" height="18px" width="auto"> Google
 | 
				
			||||||
            <button mat-flat-button class="w-100 mb-1" style="background-color: #efe8e8" (click)="authService.loginWithGoogle()">
 | 
								</button>
 | 
				
			||||||
                <img class="mr-2" src="/assets/images/google.png" height="18px" width="auto"> Google
 | 
							</div>
 | 
				
			||||||
            </button>
 | 
							<div *ngIf="authService.authenticated">
 | 
				
			||||||
        </div>
 | 
								<button mat-flat-button class="w-100 text-white mb-1 py-2" style="background-color: #dd0330" (click)="authService.logout()">
 | 
				
			||||||
        <div *ngIf="authService.authenticated">
 | 
									<mat-icon>logout</mat-icon> Logout
 | 
				
			||||||
            <button mat-flat-button class="w-100 text-white mb-1" style="background-color: #dd0330" (click)="authService.logout()">
 | 
								</button>
 | 
				
			||||||
                <mat-icon>logout</mat-icon> Logout
 | 
							</div>
 | 
				
			||||||
            </button>
 | 
							<div>
 | 
				
			||||||
        </div>
 | 
								<hr style="background-color: #FFFFFF90">
 | 
				
			||||||
        <hr style="background-color: #FFFFFF90">
 | 
							</div>
 | 
				
			||||||
        <div>
 | 
							<div>
 | 
				
			||||||
            <button *ngIf="authService.authenticated" mat-flat-button class="w-100 mb-3" style="background-color: #efe8e8" (click)="new()">
 | 
								<button *ngIf="authService.authenticated" mat-flat-button class="w-100 mb-3 py-2" style="background-color: #efe8e8" (click)="new()">
 | 
				
			||||||
                <mat-icon>history</mat-icon> Recent
 | 
									<mat-icon>history</mat-icon> Recent
 | 
				
			||||||
            </button>
 | 
								</button>
 | 
				
			||||||
            <button mat-flat-button class="w-100 text-white mb-3" style="background-color: #dd0330" (click)="new()">
 | 
								<button mat-flat-button class="w-100 text-white mb-3 py-2" style="background-color: #dd0330" (click)="new()">
 | 
				
			||||||
                <mat-icon>add</mat-icon> New Map
 | 
									<mat-icon>add</mat-icon> New Map
 | 
				
			||||||
            </button>
 | 
								</button>
 | 
				
			||||||
            <div class="w-100">
 | 
								<div class="w-100">
 | 
				
			||||||
                <form class="input-group">
 | 
									<form class="input-group">
 | 
				
			||||||
                    <input type="text" class="form-control" [(ngModel)]="code" name="code" placeholder="Code" (keyup)="isValid()">
 | 
										<input type="text" class="form-control" [(ngModel)]="code" name="code" placeholder="Code" (keyup)="isValid()">
 | 
				
			||||||
                    <div class="input-group-append">
 | 
										<div class="input-group-append">
 | 
				
			||||||
                        <button class="btn btn-danger" [routerLink]="['/', code]" style="background-color: #dd0330" [disabled]="!valid || code.length < 4">Open</button>
 | 
											<button class="btn btn-danger" [routerLink]="['/', code]" style="background-color: #dd0330" [disabled]="!valid || code.length < 4">Open</button>
 | 
				
			||||||
                    </div>
 | 
										</div>
 | 
				
			||||||
                    <small *ngIf="!valid && code.length > 3" class="mt-2 text-danger">Codes can only be made up of letters and numbers!</small>
 | 
										<small *ngIf="!valid && code.length > 3" class="mt-2 text-danger">Codes can only be made up of letters and numbers!</small>
 | 
				
			||||||
                </form>
 | 
									</form>
 | 
				
			||||||
            </div>
 | 
								</div>
 | 
				
			||||||
        </div>
 | 
							</div>
 | 
				
			||||||
    </div>
 | 
						</div>
 | 
				
			||||||
    <span class="credits text-white">
 | 
					    <span class="credits text-white p-3">
 | 
				
			||||||
        Created By <a href="https://zakscode.com" target="_blank">Zak Timson</a>
 | 
					        Created By <a href="https://zakscode.com" target="_blank">Zak Timson</a>
 | 
				
			||||||
    </span>
 | 
					    </span>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
.badge {
 | 
					 | 
				
			||||||
  position: absolute;
 | 
					 | 
				
			||||||
  top: 15%;
 | 
					 | 
				
			||||||
  left: 50%;
 | 
					 | 
				
			||||||
  z-index: 5000;
 | 
					 | 
				
			||||||
  transform: translateX(-50%);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.controls {
 | 
					.controls {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  bottom: 15%;
 | 
					  top: 50%;
 | 
				
			||||||
  left: 50%;
 | 
					  left: 50%;
 | 
				
			||||||
  width: 200px;
 | 
					  width: 200px;
 | 
				
			||||||
  z-index: 5000;
 | 
					  z-index: 5000;
 | 
				
			||||||
  transform: translateX(-50%);
 | 
					  transform: translate(-50%, -50%);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.credits {
 | 
					.credits {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  bottom: 2%;
 | 
					  bottom: 0;
 | 
				
			||||||
  right: 2%;
 | 
					  right: 0;
 | 
				
			||||||
  z-index: 5000;
 | 
					  z-index: 5000;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,8 @@ import {Component} from "@angular/core";
 | 
				
			|||||||
import {Router} from "@angular/router";
 | 
					import {Router} from "@angular/router";
 | 
				
			||||||
import {SyncService} from "../../services/sync.service";
 | 
					import {SyncService} from "../../services/sync.service";
 | 
				
			||||||
import {AuthService} from "../../services/auth.service";
 | 
					import {AuthService} from "../../services/auth.service";
 | 
				
			||||||
import {fadeIn} from "../../animations";
 | 
					import {fadeIn} from "../../utils/animations";
 | 
				
			||||||
 | 
					import {randomStringBuilder} from '../../utils/string';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 | 
					const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,7 +22,7 @@ export class HomeComponent {
 | 
				
			|||||||
    async new() {
 | 
					    async new() {
 | 
				
			||||||
        let mapCode: string;
 | 
					        let mapCode: string;
 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
            mapCode = Array(8).fill(0).map(() => chars[Math.floor(Math.random() * chars.length)]).join('');
 | 
					            mapCode = randomStringBuilder(8, true, true);
 | 
				
			||||||
        } while (await this.syncService.exists(mapCode));
 | 
					        } while (await this.syncService.exists(mapCode));
 | 
				
			||||||
        return this.router.navigate(['/', mapCode]);
 | 
					        return this.router.navigate(['/', mapCode]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,64 +1,32 @@
 | 
				
			|||||||
#map {
 | 
					#map {
 | 
				
			||||||
  height: 100vh;
 | 
						height: 100vh;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.palette {
 | 
					.palette {
 | 
				
			||||||
  position: fixed;
 | 
						position: fixed;
 | 
				
			||||||
  z-index: 5000;
 | 
						z-index: 5000;
 | 
				
			||||||
  top: 60px;
 | 
						top: 60px;
 | 
				
			||||||
  right: 15px;
 | 
						right: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.share {
 | 
					.share {
 | 
				
			||||||
  position: fixed;
 | 
						position: fixed;
 | 
				
			||||||
  z-index: 5000;
 | 
						z-index: 5000;
 | 
				
			||||||
  top: 60px;
 | 
						top: 60px;
 | 
				
			||||||
  right: 15px;
 | 
						right: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.info {
 | 
					.info {
 | 
				
			||||||
  background-color: #00000050;
 | 
						background-color: #00000050;
 | 
				
			||||||
  position: fixed;
 | 
						position: fixed;
 | 
				
			||||||
  z-index: 5000;
 | 
						z-index: 5000;
 | 
				
			||||||
  bottom: 15px;
 | 
						bottom: 15px;
 | 
				
			||||||
  left: 15px;
 | 
						left: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.gps {
 | 
					.gps {
 | 
				
			||||||
  position: fixed;
 | 
						position: fixed;
 | 
				
			||||||
  z-index: 5000;
 | 
						z-index: 5000;
 | 
				
			||||||
  bottom: 15px;
 | 
						bottom: 15px;
 | 
				
			||||||
  right: 15px;
 | 
						right: 15px;
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
::ng-deep .mat-menu-panel {
 | 
					 | 
				
			||||||
  background-color: rgba(0, 0, 0, 0.8) !important;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .mat-menu-item {
 | 
					 | 
				
			||||||
    color: rgba(255, 255, 255, 0.54);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &:hover {
 | 
					 | 
				
			||||||
      background-color: rgba(90, 90, 90, 0.8) !important;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .mat-icon-no-color {
 | 
					 | 
				
			||||||
     color: rgba(255, 255, 255, 0.54)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
::ng-deep .mat-toolbar {
 | 
					 | 
				
			||||||
  position: absolute;
 | 
					 | 
				
			||||||
  height: 45px !important;
 | 
					 | 
				
			||||||
  z-index: 5000;
 | 
					 | 
				
			||||||
  color: rgba(255, 255, 255, 0.54);
 | 
					 | 
				
			||||||
  background-color: rgba(0, 0, 0, 0.8);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .selected {
 | 
					 | 
				
			||||||
    background-color: rgba(75, 75, 75, 0.8);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .mat-icon-button:hover {
 | 
					 | 
				
			||||||
    background-color: rgba(90, 90, 90, 0.8) !important;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,20 @@
 | 
				
			|||||||
import {Component, OnDestroy, OnInit} from "@angular/core";
 | 
					import {Component, OnDestroy, OnInit} from "@angular/core";
 | 
				
			||||||
 | 
					import {PermissionsService} from '../../components/permissions/permissions.service';
 | 
				
			||||||
import {PhysicsService} from "../../services/physics.service";
 | 
					import {PhysicsService} from "../../services/physics.service";
 | 
				
			||||||
import {filter, finalize, skip, take} from "rxjs/operators";
 | 
					import {filter, finalize, skip, take} from "rxjs/operators";
 | 
				
			||||||
import {CalibrateComponent} from "../../components/calibrate/calibrate.component";
 | 
					import {CalibrateComponent} from "../../components/calibrate/calibrate.component";
 | 
				
			||||||
import {ToolbarItem} from "../../models/toolbarItem";
 | 
					import {ToolbarItem} from "../../models/toolbarItem";
 | 
				
			||||||
import {flyInRight, flyOutRight} from "../../animations";
 | 
					import {flyInRight, flyOutRight} from "../../utils/animations";
 | 
				
			||||||
import {MapLayers, MapService, WeatherLayers} from "../../services/map.service";
 | 
					import {MapLayers, MapService, WeatherLayers} from "../../services/map.service";
 | 
				
			||||||
import {Subscription} from "rxjs";
 | 
					import {Subscription} from "rxjs";
 | 
				
			||||||
import {copyToClipboard, relativeLatLng} from "../../utils";
 | 
					import {copyToClipboard, relativeLatLng} from "../../utils/misc";
 | 
				
			||||||
import {ActivatedRoute} from "@angular/router";
 | 
					import {ActivatedRoute} from "@angular/router";
 | 
				
			||||||
import {DimensionsDialogComponent} from "../../components/dimensionsDialog/dimensionsDialog.component";
 | 
					import {DimensionsDialogComponent} from "../../components/dimensionsDialog/dimensionsDialog.component";
 | 
				
			||||||
import {MatDialog} from "@angular/material/dialog";
 | 
					import {MatDialog} from "@angular/material/dialog";
 | 
				
			||||||
import {SyncService} from "../../services/sync.service";
 | 
					import {SyncService} from "../../services/sync.service";
 | 
				
			||||||
import {MapData, Marker} from "../../models/mapSymbol";
 | 
					import {MapData, Marker} from "../../models/mapSymbol";
 | 
				
			||||||
import {Adjectives} from "../../adjectives";
 | 
					import {Adjectives} from "../../models/adjectives";
 | 
				
			||||||
import {Nouns} from "../../nounes";
 | 
					import {Nouns} from "../../models/nounes";
 | 
				
			||||||
import {EditSymbolComponent} from "../../components/editSymbol/editSymbol.component";
 | 
					import {EditSymbolComponent} from "../../components/editSymbol/editSymbol.component";
 | 
				
			||||||
import {MatSnackBar} from "@angular/material/snack-bar";
 | 
					import {MatSnackBar} from "@angular/material/snack-bar";
 | 
				
			||||||
import {MatBottomSheet} from "@angular/material/bottom-sheet";
 | 
					import {MatBottomSheet} from "@angular/material/bottom-sheet";
 | 
				
			||||||
@@ -34,7 +35,7 @@ export class MapComponent implements OnDestroy, OnInit {
 | 
				
			|||||||
    map: MapService;
 | 
					    map: MapService;
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
    polygon: any;
 | 
					    polygon: any;
 | 
				
			||||||
    position;
 | 
					    position?: {heading: number, altitude: number, speed: number, latitude: number, longitude: number};
 | 
				
			||||||
    positionMarker = {arrow: null, circle: null};
 | 
					    positionMarker = {arrow: null, circle: null};
 | 
				
			||||||
    shareDialog = false;
 | 
					    shareDialog = false;
 | 
				
			||||||
    showPalette = false;
 | 
					    showPalette = false;
 | 
				
			||||||
@@ -42,7 +43,7 @@ export class MapComponent implements OnDestroy, OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    menu: ToolbarItem[];
 | 
					    menu: ToolbarItem[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(public physicsService: PhysicsService, public syncService: SyncService, private snackBar: MatSnackBar, private bottomSheet: MatBottomSheet, private dialog: MatDialog, private route: ActivatedRoute) {
 | 
					    constructor(public physicsService: PhysicsService, private permissionsService: PermissionsService, public syncService: SyncService, private snackBar: MatSnackBar, private bottomSheet: MatBottomSheet, private dialog: MatDialog, private route: ActivatedRoute) {
 | 
				
			||||||
        this.name = localStorage.getItem('callSign');
 | 
					        this.name = localStorage.getItem('callSign');
 | 
				
			||||||
        if(!this.name) {
 | 
					        if(!this.name) {
 | 
				
			||||||
            this.name = Adjectives[Math.floor(Math.random() * Adjectives.length)] + ' ' + Nouns[Math.floor(Math.random() * Nouns.length)];
 | 
					            this.name = Adjectives[Math.floor(Math.random() * Adjectives.length)] + ' ' + Nouns[Math.floor(Math.random() * Nouns.length)];
 | 
				
			||||||
@@ -75,7 +76,7 @@ export class MapComponent implements OnDestroy, OnInit {
 | 
				
			|||||||
                    {name: 'Sea Level Pressure', toggle: true, click: () => this.map.setWeatherLayer(WeatherLayers.SEA_LEVEL_PRESSURE)},
 | 
					                    {name: 'Sea Level Pressure', toggle: true, click: () => this.map.setWeatherLayer(WeatherLayers.SEA_LEVEL_PRESSURE)},
 | 
				
			||||||
                    {name: 'Clouds', toggle: true, click: () => this.map.setWeatherLayer(WeatherLayers.CLOUDS_NEW)},
 | 
					                    {name: 'Clouds', toggle: true, click: () => this.map.setWeatherLayer(WeatherLayers.CLOUDS_NEW)},
 | 
				
			||||||
                ]},
 | 
					                ]},
 | 
				
			||||||
            {name: 'Calibrate', icon: 'explore', toggle: true, onEnabled: this.startCalibrating, onDisabled: this.unsub},
 | 
					            {name: 'Calibrate', icon: 'explore', click: this.startCalibrating},
 | 
				
			||||||
            {name: 'Share', icon: 'share', toggle: true, onEnabled: () => this.share(), onDisabled: () => this.shareDialog = false},
 | 
					            {name: 'Share', icon: 'share', toggle: true, onEnabled: () => this.share(), onDisabled: () => this.shareDialog = false},
 | 
				
			||||||
            {name: 'Messages', icon: 'chat', hidden: true},
 | 
					            {name: 'Messages', icon: 'chat', hidden: true},
 | 
				
			||||||
            {name: 'Identity', icon: 'perm_identity', hidden: true},
 | 
					            {name: 'Identity', icon: 'perm_identity', hidden: true},
 | 
				
			||||||
@@ -99,7 +100,7 @@ export class MapComponent implements OnDestroy, OnInit {
 | 
				
			|||||||
        this.syncService.mapData.pipe(filter(s => !!s)).subscribe((map: MapData) => {
 | 
					        this.syncService.mapData.pipe(filter(s => !!s)).subscribe((map: MapData) => {
 | 
				
			||||||
            this.map.deleteAll();
 | 
					            this.map.deleteAll();
 | 
				
			||||||
            if (map.circles) Object.values(map.circles).filter(c => !c.deleted).forEach(c => this.map.newCircle(c));
 | 
					            if (map.circles) Object.values(map.circles).filter(c => !c.deleted).forEach(c => this.map.newCircle(c));
 | 
				
			||||||
            if (map.locations) Object.values(map.locations).forEach(l => this.map.newMarker(Object.assign(l, {icon: 'dot', noDeleteTool: true})));
 | 
								if (map.locations) Object.values(map.locations).forEach(l => this.map.newMarker({...l, icon: 'dot', noDeleteTool: true}));
 | 
				
			||||||
            if (map.markers) Object.values(map.markers).filter(m => !m.deleted).forEach(m => this.map.newMarker(m));
 | 
					            if (map.markers) Object.values(map.markers).filter(m => !m.deleted).forEach(m => this.map.newMarker(m));
 | 
				
			||||||
            if (map.measurements) Object.values(map.measurements).filter(m => !m.deleted).forEach(m => this.map.newMeasurement(m));
 | 
					            if (map.measurements) Object.values(map.measurements).filter(m => !m.deleted).forEach(m => this.map.newMeasurement(m));
 | 
				
			||||||
            if (map.polygons) Object.values(map.polygons).filter(p => !p.deleted).forEach(p => this.map.newPolygon(p));
 | 
					            if (map.polygons) Object.values(map.polygons).filter(p => !p.deleted).forEach(p => this.map.newPolygon(p));
 | 
				
			||||||
@@ -146,7 +147,7 @@ export class MapComponent implements OnDestroy, OnInit {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    center(pos?) {
 | 
					    center(pos?: any) {
 | 
				
			||||||
        if (!pos) pos = {lat: this.position.latitude, lng: this.position.longitude};
 | 
					        if (!pos) pos = {lat: this.position.latitude, lng: this.position.longitude};
 | 
				
			||||||
        this.map.centerOn(pos);
 | 
					        this.map.centerOn(pos);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -177,14 +178,21 @@ export class MapComponent implements OnDestroy, OnInit {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    startCalibrating = (menuItem?) => {
 | 
					    startCalibrating = (menuItem?) => {
 | 
				
			||||||
        this.calibration = this.bottomSheet.open(CalibrateComponent, {hasBackdrop: false, disableClose: true});
 | 
							if(this.calibration) {
 | 
				
			||||||
        this.sub = this.calibration.afterDismissed().pipe(finalize(() => {
 | 
								if(this.sub) this.sub.unsubscribe();
 | 
				
			||||||
            menuItem.enabled = false;
 | 
								this.calibration.dismiss();
 | 
				
			||||||
        })).subscribe(() => {
 | 
								this.sub = null;
 | 
				
			||||||
            this.calibration.dismiss();
 | 
								this.calibration = null;
 | 
				
			||||||
            this.calibration = null;
 | 
							} else {
 | 
				
			||||||
            this.sub = null;
 | 
								this.calibration = this.bottomSheet.open(CalibrateComponent, {hasBackdrop: false, disableClose: true});
 | 
				
			||||||
        });
 | 
								this.sub = this.calibration.afterDismissed().pipe(finalize(() => {
 | 
				
			||||||
 | 
									menuItem.enabled = false;
 | 
				
			||||||
 | 
								})).subscribe(() => {
 | 
				
			||||||
 | 
									this.calibration.dismiss();
 | 
				
			||||||
 | 
									this.calibration = null;
 | 
				
			||||||
 | 
									this.sub = null;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    startCircle = menuItem => {
 | 
					    startCircle = menuItem => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
export const environment = {
 | 
					export const environment = {
 | 
				
			||||||
  bing: 'Ah3zXeKgjcCebkqxGBZ4zROLJ_NJm7djhArju4--__5Jg9p19VgCtPkLmv-FxS_C',
 | 
					  bing: 'Ah3zXeKgjcCebkqxGBZ4zROLJ_NJm7djhArju4--__5Jg9p19VgCtPkLmv-FxS_C',
 | 
				
			||||||
  firebaseConfig: {
 | 
					  firebase: {
 | 
				
			||||||
    apiKey: "AIzaSyDFtvCY6nH_HUoTBNf_5b-E8nRweSLYtxE",
 | 
					    apiKey: "AIzaSyDFtvCY6nH_HUoTBNf_5b-E8nRweSLYtxE",
 | 
				
			||||||
    authDomain: "mapalliance-ab38a.firebaseapp.com",
 | 
					    authDomain: "mapalliance-ab38a.firebaseapp.com",
 | 
				
			||||||
    databaseURL: "https://mapalliance-ab38a.firebaseio.com",
 | 
					    databaseURL: "https://mapalliance-ab38a.firebaseio.com",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,6 @@
 | 
				
			|||||||
// This file can be replaced during build by using the `fileReplacements` array.
 | 
					 | 
				
			||||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
 | 
					 | 
				
			||||||
// The list of file replacements can be found in `angular.json`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const environment = {
 | 
					export const environment = {
 | 
				
			||||||
  bing: 'Ah3zXeKgjcCebkqxGBZ4zROLJ_NJm7djhArju4--__5Jg9p19VgCtPkLmv-FxS_C',
 | 
					  bing: 'Ah3zXeKgjcCebkqxGBZ4zROLJ_NJm7djhArju4--__5Jg9p19VgCtPkLmv-FxS_C',
 | 
				
			||||||
  firebaseConfig: {
 | 
					  firebase: {
 | 
				
			||||||
    apiKey: "AIzaSyDFtvCY6nH_HUoTBNf_5b-E8nRweSLYtxE",
 | 
					    apiKey: "AIzaSyDFtvCY6nH_HUoTBNf_5b-E8nRweSLYtxE",
 | 
				
			||||||
    authDomain: "mapalliance-ab38a.firebaseapp.com",
 | 
					    authDomain: "mapalliance-ab38a.firebaseapp.com",
 | 
				
			||||||
    databaseURL: "https://mapalliance-ab38a.firebaseio.com",
 | 
					    databaseURL: "https://mapalliance-ab38a.firebaseio.com",
 | 
				
			||||||
@@ -17,12 +13,3 @@ export const environment = {
 | 
				
			|||||||
  openWeather: 'e8391af54b6fc09dc82b019fc68b8409',
 | 
					  openWeather: 'e8391af54b6fc09dc82b019fc68b8409',
 | 
				
			||||||
  production: false
 | 
					  production: false
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * For easier debugging in development mode, you can import the following file
 | 
					 | 
				
			||||||
 * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This import should be commented out in production mode because it will have a negative impact
 | 
					 | 
				
			||||||
 * on performance if an error is thrown.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
// import 'zone.js/dist/zone-error';  // Included with Angular CLI.
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,36 +1,37 @@
 | 
				
			|||||||
<!doctype html>
 | 
					<!doctype html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
<head>
 | 
					<head>
 | 
				
			||||||
  <title>Map Alliance</title>
 | 
						<title>Map Alliance</title>
 | 
				
			||||||
  <base href="/">
 | 
						<base href="/">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <meta charset="utf-8">
 | 
						<!-- Generic -->
 | 
				
			||||||
  <meta name="viewport" content="width=device-width, user-scalable=no">
 | 
						<meta charset="utf-8">
 | 
				
			||||||
  <meta name="theme-color" content="#000000">
 | 
						<meta name="viewport" content="width=device-width, user-scalable=no">
 | 
				
			||||||
  <meta name="description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
						<meta name="theme-color" content="#000000">
 | 
				
			||||||
  <!-- Google / Search Engine Tags -->
 | 
						<meta name="description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
				
			||||||
  <meta itemprop="name" content="Map Alliance">
 | 
						<!-- Google / Search Engine Tags -->
 | 
				
			||||||
  <meta itemprop="description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
						<meta itemprop="name" content="Map Alliance">
 | 
				
			||||||
  <meta itemprop="image" content="/assets/icons/icon-512x512.png">
 | 
						<meta itemprop="description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
				
			||||||
  <!-- Facebook Meta Tags -->
 | 
						<meta itemprop="image" content="/assets/icons/icon-512x512.png">
 | 
				
			||||||
  <meta property="og:type" content="website">
 | 
						<!-- Facebook Meta Tags -->
 | 
				
			||||||
  <meta property="og:title" content="Map Alliance">
 | 
						<meta property="og:type" content="website">
 | 
				
			||||||
  <meta property="og:description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
						<meta property="og:title" content="Map Alliance">
 | 
				
			||||||
  <meta property="og:image" content="/assets/icons/icon-512x512.png">
 | 
						<meta property="og:description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
				
			||||||
  <!-- Twitter Meta Tags -->
 | 
						<meta property="og:image" content="/assets/icons/icon-512x512.png">
 | 
				
			||||||
  <meta name="twitter:card" content="summary_large_image">
 | 
						<!-- Twitter Meta Tags -->
 | 
				
			||||||
  <meta name="twitter:title" content="Map Alliance">
 | 
						<meta name="twitter:card" content="summary_large_image">
 | 
				
			||||||
  <meta name="twitter:description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
						<meta name="twitter:title" content="Map Alliance">
 | 
				
			||||||
  <meta name="twitter:image" content="/assets/icons/icon-512x512.png">
 | 
						<meta name="twitter:description" content="View & edit maps collaborative with real time GPS positioning.">
 | 
				
			||||||
 | 
						<meta name="twitter:image" content="/assets/icons/icon-512x512.png">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <link rel="icon" type="image/x-icon" href="/assets/images/logo.png">
 | 
						<link rel="icon" type="image/x-icon" href="/assets/images/logo.png">
 | 
				
			||||||
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500|Material+Icons" rel="stylesheet">
 | 
						<link rel="manifest" href="manifest.json">
 | 
				
			||||||
  <link rel="manifest" href="manifest.webmanifest">
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDFtvCY6nH_HUoTBNf_5b-E8nRweSLYtxE" async defer></script>
 | 
						<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDFtvCY6nH_HUoTBNf_5b-E8nRweSLYtxE"></script>
 | 
				
			||||||
 | 
					  <meta name="theme-color" content="#1976d2">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
  <app-root></app-root>
 | 
					<app-root></app-root>
 | 
				
			||||||
  <noscript>Please enable JavaScript to continue using this application.</noscript>
 | 
					  <noscript>Please enable JavaScript to continue using this application.</noscript>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										89
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								src/main.ts
									
									
									
									
									
								
							@@ -1,61 +1,58 @@
 | 
				
			|||||||
import 'hammerjs';
 | 
					 | 
				
			||||||
import {enableProdMode} from '@angular/core';
 | 
					 | 
				
			||||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
 | 
					import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
 | 
				
			||||||
 | 
					import {initializeApp} from 'firebase/app';
 | 
				
			||||||
 | 
					import 'hammerjs';
 | 
				
			||||||
import {AppModule} from './app/app.module';
 | 
					import {AppModule} from './app/app.module';
 | 
				
			||||||
import {environment} from './environments/environment';
 | 
					import {environment} from './environments/environment';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (environment.production) {
 | 
					initializeApp(environment.firebase);
 | 
				
			||||||
    enableProdMode();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
platformBrowserDynamic().bootstrapModule(AppModule)
 | 
					platformBrowserDynamic().bootstrapModule(AppModule)
 | 
				
			||||||
    .catch(err => console.error(err));
 | 
						.catch(err => console.error(err));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// leaflet ===========================================
 | 
					// Leaflet touch polyfill ===========================================
 | 
				
			||||||
declare const L;
 | 
					declare const L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Touch support
 | 
					// Touch support
 | 
				
			||||||
L.Map.mergeOptions({touchExtend: true});
 | 
					L.Map.mergeOptions({touchExtend: true});
 | 
				
			||||||
L.Map.TouchExtend = L.Handler.extend({
 | 
					L.Map.TouchExtend = L.Handler.extend({
 | 
				
			||||||
    initialize: function (map) {
 | 
						initialize: function (map) {
 | 
				
			||||||
        this._map = map;
 | 
							this._map = map;
 | 
				
			||||||
        this._container = map._container;
 | 
							this._container = map._container;
 | 
				
			||||||
        this._pane = map._panes.overlayPane;
 | 
							this._pane = map._panes.overlayPane;
 | 
				
			||||||
    },
 | 
						},
 | 
				
			||||||
    addHooks: function () {
 | 
						addHooks: function () {
 | 
				
			||||||
        L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);
 | 
							L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);
 | 
				
			||||||
        L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);
 | 
							L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);
 | 
				
			||||||
        L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);
 | 
							L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);
 | 
				
			||||||
    },
 | 
						},
 | 
				
			||||||
    removeHooks: function () {
 | 
						removeHooks: function () {
 | 
				
			||||||
        L.DomEvent.off(this._container, 'touchstart', this._onTouchStart);
 | 
							L.DomEvent.off(this._container, 'touchstart', this._onTouchStart);
 | 
				
			||||||
        L.DomEvent.off(this._container, 'touchend', this._onTouchEnd);
 | 
							L.DomEvent.off(this._container, 'touchend', this._onTouchEnd);
 | 
				
			||||||
        L.DomEvent.off(this._container, 'touchmove', this._onTouchMove);
 | 
							L.DomEvent.off(this._container, 'touchmove', this._onTouchMove);
 | 
				
			||||||
    },
 | 
						},
 | 
				
			||||||
    _eventWrapper: function(e) {
 | 
						_eventWrapper: function(e) {
 | 
				
			||||||
        let containerPoint = this._map.mouseEventToContainerPoint(e);
 | 
							let containerPoint = this._map.mouseEventToContainerPoint(e);
 | 
				
			||||||
        let layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 | 
							let layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 | 
				
			||||||
        let latlng = this._map.layerPointToLatLng(layerPoint);
 | 
							let latlng = this._map.layerPointToLatLng(layerPoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
							return {
 | 
				
			||||||
            latlng: latlng,
 | 
								latlng: latlng,
 | 
				
			||||||
            layerPoint: layerPoint,
 | 
								layerPoint: layerPoint,
 | 
				
			||||||
            containerPoint: containerPoint,
 | 
								containerPoint: containerPoint,
 | 
				
			||||||
            originalEvent: e
 | 
								originalEvent: e
 | 
				
			||||||
        }
 | 
							}
 | 
				
			||||||
    },
 | 
						},
 | 
				
			||||||
    _onTouchStart: function (e) {
 | 
						_onTouchStart: function (e) {
 | 
				
			||||||
        if (!this._map._loaded) return;
 | 
							if (!this._map._loaded) return;
 | 
				
			||||||
        this._map.fire('touchstart', this._eventWrapper(e));
 | 
							this._map.fire('touchstart', this._eventWrapper(e));
 | 
				
			||||||
    },
 | 
						},
 | 
				
			||||||
    _onTouchEnd: function (e) {
 | 
						_onTouchEnd: function (e) {
 | 
				
			||||||
        if (!this._map._loaded) return;
 | 
							if (!this._map._loaded) return;
 | 
				
			||||||
        this._map.fire('touchend', this._eventWrapper(e));
 | 
							this._map.fire('touchend', this._eventWrapper(e));
 | 
				
			||||||
    },
 | 
						},
 | 
				
			||||||
    _onTouchMove: function(e) {
 | 
						_onTouchMove: function(e) {
 | 
				
			||||||
        if(!this._map._loaded) return;
 | 
							if(!this._map._loaded) return;
 | 
				
			||||||
        this._map.fire('touchmove', this._eventWrapper(e));
 | 
							this._map.fire('touchmove', this._eventWrapper(e));
 | 
				
			||||||
    }
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);
 | 
					L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										51
									
								
								src/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/manifest.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"name": "Map Alliance",
 | 
				
			||||||
 | 
						"short_name": "Map Alliance",
 | 
				
			||||||
 | 
						"start_url": "/",
 | 
				
			||||||
 | 
						"scope": "/",
 | 
				
			||||||
 | 
						"display": "standalone",
 | 
				
			||||||
 | 
						"theme_color": "#000000",
 | 
				
			||||||
 | 
						"background_color": "#000000",
 | 
				
			||||||
 | 
						"icons": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-72x72.png",
 | 
				
			||||||
 | 
								"sizes": "72x72",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-96x96.png",
 | 
				
			||||||
 | 
								"sizes": "96x96",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-128x128.png",
 | 
				
			||||||
 | 
								"sizes": "128x128",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-144x144.png",
 | 
				
			||||||
 | 
								"sizes": "144x144",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-152x152.png",
 | 
				
			||||||
 | 
								"sizes": "152x152",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-192x192.png",
 | 
				
			||||||
 | 
								"sizes": "192x192",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-384x384.png",
 | 
				
			||||||
 | 
								"sizes": "384x384",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"src": "assets/icons/icon-512x512.png",
 | 
				
			||||||
 | 
								"sizes": "512x512",
 | 
				
			||||||
 | 
								"type": "image/png"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,51 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "name": "Map Alliance",
 | 
					 | 
				
			||||||
  "short_name": "Map Alliance",
 | 
					 | 
				
			||||||
  "theme_color": "#000000",
 | 
					 | 
				
			||||||
  "background_color": "#000000",
 | 
					 | 
				
			||||||
  "display": "standalone",
 | 
					 | 
				
			||||||
  "scope": "/",
 | 
					 | 
				
			||||||
  "start_url": "/",
 | 
					 | 
				
			||||||
  "icons": [
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-72x72.png",
 | 
					 | 
				
			||||||
      "sizes": "72x72",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-96x96.png",
 | 
					 | 
				
			||||||
      "sizes": "96x96",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-128x128.png",
 | 
					 | 
				
			||||||
      "sizes": "128x128",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-144x144.png",
 | 
					 | 
				
			||||||
      "sizes": "144x144",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-152x152.png",
 | 
					 | 
				
			||||||
      "sizes": "152x152",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-192x192.png",
 | 
					 | 
				
			||||||
      "sizes": "192x192",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-384x384.png",
 | 
					 | 
				
			||||||
      "sizes": "384x384",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "src": "assets/icons/icon-512x512.png",
 | 
					 | 
				
			||||||
      "sizes": "512x512",
 | 
					 | 
				
			||||||
      "type": "image/png"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,63 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * This file includes polyfills needed by Angular and is loaded before the app.
 | 
					 | 
				
			||||||
 * You can add your own extra polyfills to this file.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This file is divided into 2 sections:
 | 
					 | 
				
			||||||
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 | 
					 | 
				
			||||||
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 | 
					 | 
				
			||||||
 *      file.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 | 
					 | 
				
			||||||
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 | 
					 | 
				
			||||||
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Learn more in https://angular.io/guide/browser-support
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/***************************************************************************************************
 | 
					 | 
				
			||||||
 * BROWSER POLYFILLS
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
 | 
					 | 
				
			||||||
import 'classlist.js';  // Run `npm install --save classlist.js`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Web Animations `@angular/platform-browser/animations`
 | 
					 | 
				
			||||||
 * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
 | 
					 | 
				
			||||||
 * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
import 'web-animations-js';  // Run `npm install --save web-animations-js`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * By default, zone.js will patch all possible macroTask and DomEvents
 | 
					 | 
				
			||||||
 * user can disable parts of macroTask/DomEvents patch by setting following flags
 | 
					 | 
				
			||||||
 * because those flags need to be set before `zone.js` being loaded, and webpack
 | 
					 | 
				
			||||||
 * will put import in the top of bundle, so user need to create a separate file
 | 
					 | 
				
			||||||
 * in this directory (for example: zone-flags.ts), and put the following flags
 | 
					 | 
				
			||||||
 * into that file, and then add the following code before importing zone.js.
 | 
					 | 
				
			||||||
 * import './zone-flags.ts';
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The flags allowed in zone-flags.ts are listed here.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The following flags will work for all browsers.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 | 
					 | 
				
			||||||
 * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 | 
					 | 
				
			||||||
 * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 | 
					 | 
				
			||||||
 *  with the following flag, it will bypass `zone.js` patch for IE/Edge
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *  (window as any).__Zone_enable_cross_context_check = true;
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/***************************************************************************************************
 | 
					 | 
				
			||||||
 * Zone JS is required by default for Angular itself.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
import 'zone.js/dist/zone';  // Included with Angular CLI.
 | 
					 | 
				
			||||||
import 'whatwg-fetch'; // Run `npm install --save whatwg-fetch`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/***************************************************************************************************
 | 
					 | 
				
			||||||
 * APPLICATION IMPORTS
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
							
								
								
									
										151
									
								
								src/styles.scss
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								src/styles.scss
									
									
									
									
									
								
							@@ -1,101 +1,130 @@
 | 
				
			|||||||
@import '../node_modules/@angular/material/theming';
 | 
					@use '@angular/material' as mat;
 | 
				
			||||||
 | 
					@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500|Material+Icons");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@include mat-core();
 | 
					@include mat.core();
 | 
				
			||||||
 | 
					$my-theme: mat.define-light-theme((
 | 
				
			||||||
$custom-theme-primary: mat-palette($mat-blue, 600);
 | 
						color: (
 | 
				
			||||||
$custom-theme-accent: mat-palette($mat-red, 600);
 | 
							primary: mat.define-palette(mat.$blue-palette, 600),
 | 
				
			||||||
$custom-theme-warn: mat-palette($mat-orange, 600);
 | 
							accent: mat.define-palette(mat.$red-palette, 600),
 | 
				
			||||||
 | 
						),
 | 
				
			||||||
$custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn);
 | 
						typography: mat.define-typography-config(),
 | 
				
			||||||
 | 
						density: 0,
 | 
				
			||||||
@include angular-material-theme($custom-theme);
 | 
					));
 | 
				
			||||||
 | 
					@include mat.all-component-themes($my-theme);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:focus {
 | 
					:focus {
 | 
				
			||||||
  outline: none !important;
 | 
						outline: none !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html, body {
 | 
					html, body {
 | 
				
			||||||
  height: 100vh;
 | 
						height: 100vh;
 | 
				
			||||||
  overflow: hidden;
 | 
						overflow: hidden;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
  margin: 0;
 | 
						margin: 0;
 | 
				
			||||||
  overscroll-behavior: none;
 | 
						overscroll-behavior: none;
 | 
				
			||||||
  font-family: Roboto, "Helvetica Neue", sans-serif;
 | 
						font-family: Roboto, "Helvetica Neue", sans-serif;
 | 
				
			||||||
  background-color: black;
 | 
						background-color: black;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.curs-pointer {
 | 
					.curs-pointer {
 | 
				
			||||||
  cursor: pointer;
 | 
						cursor: pointer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.d-relative {
 | 
					.d-relative {
 | 
				
			||||||
  position: relative;
 | 
						position: relative;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.overflow-hidden {
 | 
					.overflow-hidden {
 | 
				
			||||||
  overflow: hidden;
 | 
						overflow: hidden;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.cdk-overlay-pane.p-0 {
 | 
					.cdk-overlay-pane.p-0 {
 | 
				
			||||||
  .mat-dialog-container {
 | 
						.mat-dialog-container {
 | 
				
			||||||
    padding: 0;
 | 
							padding: 0;
 | 
				
			||||||
    overflow: hidden;
 | 
							overflow: hidden;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.cdk-overlay-pane.pb-0 {
 | 
					.cdk-overlay-pane.pb-0 {
 | 
				
			||||||
  .mat-dialog-container {
 | 
						.mat-dialog-container {
 | 
				
			||||||
    padding-bottom: 0;
 | 
							padding-bottom: 0;
 | 
				
			||||||
    overflow: hidden;
 | 
							overflow: hidden;
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
a[href^="http://maps.google.com/maps"]{display:none !important}
 | 
					 | 
				
			||||||
a[href^="https://maps.google.com/maps"]{display:none !important}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.gmnoprint a, .gmnoprint span, .gm-style-cc {
 | 
					 | 
				
			||||||
  display: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.gmnoprint div {
 | 
					 | 
				
			||||||
  background: none !important;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.cdk-overlay-container {
 | 
					.cdk-overlay-container {
 | 
				
			||||||
  z-index: 5500 !important;
 | 
						z-index: 5500 !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.leaflet-tooltip {
 | 
					.mat-mdc-menu-panel {
 | 
				
			||||||
  background: rgba(0, 0, 0, 0.6) !important;
 | 
						background-color: rgba(0, 0, 0, 0.8) !important;
 | 
				
			||||||
  border-color: rgba(0, 0, 0, 0.6) !important;
 | 
					 | 
				
			||||||
  color: white;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.leaflet-tooltip-right:before {
 | 
						.mat-mdc-menu-item {
 | 
				
			||||||
    border-right-color: rgba(0, 0, 0, 0.6) !important;
 | 
							color: rgba(255, 255, 255, 0.54);
 | 
				
			||||||
  }
 | 
					
 | 
				
			||||||
  &.leaflet-tooltip-bottom:before {
 | 
							&:hover {
 | 
				
			||||||
    border-bottom-color: rgba(0, 0, 0, 0.6) !important;
 | 
								background-color: rgba(90, 90, 90, 0.8) !important;
 | 
				
			||||||
  }
 | 
							}
 | 
				
			||||||
  &.leaflet-tooltip-top:before {
 | 
					
 | 
				
			||||||
    border-top-color: rgba(0, 0, 0, 0.6) !important;
 | 
							.mat-icon-no-color {
 | 
				
			||||||
  }
 | 
								color: rgba(255, 255, 255, 0.54)
 | 
				
			||||||
  &.leaflet-tooltip-left:before {
 | 
							}
 | 
				
			||||||
    border-left-color: rgba(0, 0, 0, 0.6) !important;
 | 
						}
 | 
				
			||||||
  }
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hide watermarks =============================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.leaflet-tooltip {
 | 
				
			||||||
 | 
						background: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
						border-color: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
						color: white;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.leaflet-tooltip-right:before {
 | 
				
			||||||
 | 
							border-right-color: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.leaflet-tooltip-bottom:before {
 | 
				
			||||||
 | 
							border-bottom-color: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.leaflet-tooltip-top:before {
 | 
				
			||||||
 | 
							border-top-color: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.leaflet-tooltip-left:before {
 | 
				
			||||||
 | 
							border-left-color: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.leaflet-tooltip-bottom:before {
 | 
					.leaflet-tooltip-bottom:before {
 | 
				
			||||||
  display: none;
 | 
						display: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.leaflet-popup-content-wrapper {
 | 
					.leaflet-popup-content-wrapper {
 | 
				
			||||||
  background: rgba(0, 0, 0, 0.6) !important;
 | 
						background: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
  color: white !important;
 | 
						color: white !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.leaflet-popup-tip {
 | 
					.leaflet-popup-tip {
 | 
				
			||||||
  background: rgba(0, 0, 0, 0.6) !important;
 | 
						background: rgba(0, 0, 0, 0.6) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a[href^="http://maps.google.com/maps"] {
 | 
				
			||||||
 | 
						display: none !important
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a[href^="https://maps.google.com/maps"] {
 | 
				
			||||||
 | 
						display: none !important
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.gmnoprint a, .gmnoprint span, .gm-style-cc {
 | 
				
			||||||
 | 
						display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.gmnoprint div {
 | 
				
			||||||
 | 
						background: none !important;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +1,42 @@
 | 
				
			|||||||
 | 
					/* To learn more about this file see: https://angular.io/config/tsconfig. */
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "compileOnSave": false,
 | 
						"compileOnSave": false,
 | 
				
			||||||
  "compilerOptions": {
 | 
						"compilerOptions": {
 | 
				
			||||||
    "baseUrl": "./",
 | 
							"outDir": "./out-tsc/app",
 | 
				
			||||||
    "outDir": "./out-tsc/app",
 | 
							"forceConsistentCasingInFileNames": true,
 | 
				
			||||||
    "resolveJsonModule": true,
 | 
							"strict": true,
 | 
				
			||||||
    "sourceMap": true,
 | 
							"noImplicitOverride": true,
 | 
				
			||||||
    "declaration": false,
 | 
							"noPropertyAccessFromIndexSignature": true,
 | 
				
			||||||
    "downlevelIteration": true,
 | 
							"noImplicitReturns": true,
 | 
				
			||||||
    "module": "esnext",
 | 
							"noFallthroughCasesInSwitch": true,
 | 
				
			||||||
    "moduleResolution": "node",
 | 
							"skipLibCheck": true,
 | 
				
			||||||
    "experimentalDecorators": true,
 | 
							"esModuleInterop": true,
 | 
				
			||||||
    "importHelpers": true,
 | 
							"sourceMap": true,
 | 
				
			||||||
    "target": "es2015",
 | 
							"noImplicitAny": false,
 | 
				
			||||||
    "typeRoots": [
 | 
							"declaration": false,
 | 
				
			||||||
      "node_modules/@types"
 | 
							"experimentalDecorators": true,
 | 
				
			||||||
    ],
 | 
							"moduleResolution": "node",
 | 
				
			||||||
    "lib": [
 | 
							"importHelpers": true,
 | 
				
			||||||
      "es2018",
 | 
							"target": "ES2022",
 | 
				
			||||||
      "dom"
 | 
							"module": "ES2022",
 | 
				
			||||||
    ],
 | 
							"useDefineForClassFields": false,
 | 
				
			||||||
    "include": [
 | 
							"strictNullChecks": false,
 | 
				
			||||||
      "src/**/*.ts"
 | 
							"resolveJsonModule": true,
 | 
				
			||||||
    ],
 | 
							"lib": [
 | 
				
			||||||
    "exclude": [
 | 
								"ES2022",
 | 
				
			||||||
      "src/test.ts",
 | 
								"dom"
 | 
				
			||||||
      "src/**/*.spec.ts"
 | 
							]
 | 
				
			||||||
    ]
 | 
						},
 | 
				
			||||||
  }
 | 
						"angularCompilerOptions": {
 | 
				
			||||||
 | 
							"enableI18nLegacyMessageIdFormat": false,
 | 
				
			||||||
 | 
							"strictInjectionParameters": true,
 | 
				
			||||||
 | 
							"strictInputAccessModifiers": true,
 | 
				
			||||||
 | 
							"strictTemplates": true
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"files": [
 | 
				
			||||||
 | 
							"src/main.ts"
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"include": [
 | 
				
			||||||
 | 
							"src/**/*.d.ts"
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user