Reformat and fix domain error

This commit is contained in:
moo 2023-06-27 11:39:15 -04:00
parent 3b1e0e0c35
commit c3924c742e
12 changed files with 297 additions and 236 deletions

View File

@ -5,9 +5,11 @@
</div> </div>
# Overview # Overview
Upload 360° photos to Google Maps without using Google's app. Upload 360° photos to Google Maps without using Google's app.
The main reason why I created this is because Google Maps isn't a good replacement of the former app. You are only able to upload 360 photos to specific places on the map. With this project, it is possible to upload them at any coordinates. The main reason why I created this is because Google Maps isn't a good replacement of the former app. You are only able
to upload 360 photos to specific places on the map. With this project, it is possible to upload them at any coordinates.
If you want to try it out, there are publicly available instances: If you want to try it out, there are publicly available instances:
| **URL** | **Country** | **Status** | **Hosted By** | | **URL** | **Country** | **Status** | **Hosted By** |
@ -16,12 +18,16 @@ If you want to try it out, there are publicly available instances:
| [map.winscloud.net](https://map.winscloud.net) | 🇹🇭 | Up | @WinsDominoes | | [map.winscloud.net](https://map.winscloud.net) | 🇹🇭 | Up | @WinsDominoes |
## Quick start ## Quick start
In order to get the Google api keys required for the oauth follow these steps: In order to get the Google api keys required for the oauth follow these steps:
### Client ID ### Client ID
1) Create a new project at https://console.cloud.google.com/ 1) Create a new project at https://console.cloud.google.com/
2) Head over to the API library and enable the <a href="https://console.cloud.google.com/apis/library/streetviewpublish.googleapis.com">Street View Publish API</a> 2) Head over to the API library and enable
the <a href="https://console.cloud.google.com/apis/library/streetviewpublish.googleapis.com">Street View Publish
API</a>
3) Select 'Create Credentials', select 'User Data', enter any app name/email. 3) Select 'Create Credentials', select 'User Data', enter any app name/email.
@ -29,16 +35,21 @@ In order to get the Google api keys required for the oauth follow these steps:
5) Select 'Web application' for type, and name it anything. 5) Select 'Web application' for type, and name it anything.
6) Add http://localhost:7000 to authorized javascript origins. (If you are running on another domain/port, replace localhost) 6) Add http://localhost:7000 to authorized javascript origins. (If you are running on another domain/port, replace
localhost)
7) Add http://localhost:7000/auth/ to authorized redirect URIs (If you are running on another domain/port, replace localhost) 7) Add http://localhost:7000/auth/ to authorized redirect URIs (If you are running on another domain/port, replace
localhost)
8) You should now copy the Client ID into the config.json file. 8) You should now copy the Client ID into the config.json file.
### Client Secret ### Client Secret
Head to the main credentials screen and click the pencil. (Edit OAuth Client) You will be able to find the Client Secret and copy that to your config.json.
Head to the main credentials screen and click the pencil. (Edit OAuth Client) You will be able to find the Client Secret
and copy that to your config.json.
### Consent Screen ### Consent Screen
1) Select 'OAuth consent screen', use any name/email 1) Select 'OAuth consent screen', use any name/email
2) Make sure the authorized domain and email is correct, then select 'Save and Continue'. 2) Make sure the authorized domain and email is correct, then select 'Save and Continue'.
@ -47,12 +58,16 @@ Head to the main credentials screen and click the pencil. (Edit OAuth Client) Yo
4) For test users, add the email address of the account where you want to upload 360 photos. 4) For test users, add the email address of the account where you want to upload 360 photos.
5) **Make sure to also copy the client ID into index.html**, after '&client_id=' and before '&scope', if needed, change the port and domain here as well. 5) **Make sure to also copy the client ID into index.html**, after '&client_id=' and before '&scope', if needed, change
the port and domain here as well.
### Config File ### Config File
There are many options in the config file that might confuse you, so here's a simple guide (I think?) to help you get through it.
| **Keys** | **Default values** | **Usage** | There are many options in the config file that might confuse you, so here's a simple guide (I think?) to help you get
through it.
| **Keys** | **Default values** | **
Usage** |
|------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------| |------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `https` | `false` (boolean) | This is an option whether if you want to enable https or not, this uses boolean values. `false` = http:// and `true` = https:// | | `https` | `false` (boolean) | This is an option whether if you want to enable https or not, this uses boolean values. `false` = http:// and `true` = https:// |
| `host` | `localhost` (string) | Your host or domain, if you are hosting on a local machine, set this to `localhost`, if you are hosting this publicly, set it to your domain. | | `host` | `localhost` (string) | Your host or domain, if you are hosting on a local machine, set this to `localhost`, if you are hosting this publicly, set it to your domain. |
@ -61,13 +76,16 @@ There are many options in the config file that might confuse you, so here's a si
| `clientId` | string | Paste your Google OAuth Client ID here, check the previous steps on how to get it. | | `clientId` | string | Paste your Google OAuth Client ID here, check the previous steps on how to get it. |
| `clientSecret` | string | Paste your Google OAuth Client Secret ID here, check the previous steps on how to get it. | | `clientSecret` | string | Paste your Google OAuth Client Secret ID here, check the previous steps on how to get it. |
**INFO: Your authorized JavaScript origins and authorized redirect URIs should be the same as the one you have set in your config file.** **INFO: Your authorized JavaScript origins and authorized redirect URIs should be the same as the one you have set in
your config file.**
**Scenario 1 - Public Instance** **Scenario 1 - Public Instance**
This example is for those who are hosting a public instance, your https, host and port would look something like this. Change `maps.moomoo.me` to your domain. This example is for those who are hosting a public instance, your https, host and port would look something like this.
Change `maps.moomoo.me` to your domain.
You must use a nginx reverse proxy pointing to localhost:7000 for this to work. You must use a nginx reverse proxy pointing to localhost:7000 for this to work.
``` ```
{ {
"https": true, "https": true,
@ -75,13 +93,16 @@ You must use a nginx reverse proxy pointing to localhost:7000 for this to work.
"port": 7000 "port": 7000
} }
``` ```
Your **authorized JavaScript origin** would be: `https://maps.moomoo.me` Your **authorized JavaScript origin** would be: `https://maps.moomoo.me`
Your **authorized redirect URIs** would be: `https://maps.moomoo.me/auth/` (don't forget the slash after `auth`) Your **authorized redirect URIs** would be: `https://maps.moomoo.me/auth/` (don't forget the slash after `auth`)
**Scenario 2 - Private Instance** **Scenario 2 - Private Instance**
This example is for those who are running PhotoSphereStudio on a local machine, your https, host and port would look something like this. This example is for those who are running PhotoSphereStudio on a local machine, your https, host and port would look
something like this.
``` ```
{ {
"https": false, "https": false,
@ -89,9 +110,12 @@ This example is for those who are running PhotoSphereStudio on a local machine,
"port" 7000 "port" 7000
} }
``` ```
Your **authorized JavaScript origin** would be: `http://localhost:7000` Your **authorized JavaScript origin** would be: `http://localhost:7000`
Your **authorized redirect URIs** would be: `http://localhost:7000/auth/` (don't forget the slash after `auth`) Your **authorized redirect URIs** would be: `http://localhost:7000/auth/` (don't forget the slash after `auth`)
## Support ## Support
If you have any questions about how to set this up or about the source code, feel free to ask either **moom0o** (moo#0529 on Discord) or **WinsDominoes** (Win#7206 on Discord).
If you have any questions about how to set this up or about the source code, feel free to ask either **moom0o** (
moo#0529 on Discord) or **WinsDominoes** (Win#7206 on Discord).

View File

@ -6,13 +6,13 @@ const openWebBrowser = config.openWebBrowser; // Set to false if running as a se
let full_url = ""; let full_url = "";
let protocol = ""; let protocol = "";
if(config.https) { if (config.https) {
protocol = "https://" protocol = "https://"
} else { } else {
protocol = "http://" protocol = "http://"
} }
if(port && !config.https) { if (port && !config.https) {
full_url = protocol + host + ":" + port full_url = protocol + host + ":" + port
} else { } else {
full_url = protocol + host full_url = protocol + host
@ -59,11 +59,12 @@ app.use(favicon(__dirname + '/public/assets/icons/favicon.ico'));
app.get('/', function (req, res) { app.get('/', function (req, res) {
res.render('pages/index', { res.render('pages/index', {
full_url: full_url, full_url: full_url,
clientId: clientId clientId: clientId,
domain: config.host
}); });
}) })
app.get('/upload', function(req, res){ app.get('/upload', function (req, res) {
res.render('pages/upload'); res.render('pages/upload');
}) })
@ -205,7 +206,7 @@ app.get('/auth', function (req, res) {
request(options, function (error, response) { request(options, function (error, response) {
if (error) console.log(error) && res.send("Error: Check console"); if (error) console.log(error) && res.send("Error: Check console");
let body = JSON.parse(response.body) let body = JSON.parse(response.body)
if(body["error"] || !body["access_token"]){ if (body["error"] || !body["access_token"]) {
res.redirect('/') res.redirect('/')
} else { } else {
res.cookie('oauth', JSON.parse(response.body)["access_token"], { res.cookie('oauth', JSON.parse(response.body)["access_token"], {

View File

@ -13,6 +13,7 @@
"body-parser": "^1.20.1", "body-parser": "^1.20.1",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"ejs": "^3.1.9", "ejs": "^3.1.9",
"exif-js": "^2.3.0",
"express": "^4.18.2", "express": "^4.18.2",
"express-fileupload": "^1.4.0", "express-fileupload": "^1.4.0",
"open": "^8.4.0", "open": "^8.4.0",

View File

@ -25,13 +25,13 @@ map.addLayer(layer);
// Marker // Marker
let marker = null; let marker = null;
map.on('click', (event)=> { map.on('click', (event) => {
if(marker !== null){ if (marker !== null) {
map.removeLayer(marker); map.removeLayer(marker);
} }
marker = L.marker([event.latlng.lat , event.latlng.lng], {icon: defaultIcon}).addTo(map); marker = L.marker([event.latlng.lat, event.latlng.lng], {icon: defaultIcon}).addTo(map);
document.getElementById('lat').value = event.latlng.lat; document.getElementById('lat').value = event.latlng.lat;
document.getElementById('long').value = event.latlng.lng; document.getElementById('long').value = event.latlng.lng;

View File

@ -2,6 +2,7 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
button { button {
border: none; border: none;
border-radius: 2px; border-radius: 2px;
@ -14,39 +15,48 @@ button {
box-shadow: 0 0 4px #999; box-shadow: 0 0 4px #999;
outline: none; outline: none;
} }
pre { pre {
white-space: pre-line; white-space: pre-line;
} }
form, input, label, p { form, input, label, p {
color: white !important; color: white !important;
} }
#icon { #icon {
max-width: 45px; max-width: 45px;
height: auto; height: auto;
border-radius: 50%; border-radius: 50%;
} }
.column { .column {
flex-grow: 1; flex-grow: 1;
display: inline-block; display: inline-block;
} }
/* Ripple effect */ /* Ripple effect */
.ripple { .ripple {
background-position: center; background-position: center;
transition: background 0.8s; transition: background 0.8s;
} }
.ripple:hover { .ripple:hover {
background: #47a7f5 radial-gradient(circle, transparent 1%, #47a7f5 1%) center/15000%; background: #47a7f5 radial-gradient(circle, transparent 1%, #47a7f5 1%) center/15000%;
} }
.ripple:active { .ripple:active {
background-color: #6eb9f7; background-color: #6eb9f7;
background-size: 100%; background-size: 100%;
transition: background 0s; transition: background 0s;
} }
/* DRAG AND DROP */ /* DRAG AND DROP */
.file-upload { .file-upload {
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
} }
.file-upload-btn { .file-upload-btn {
width: 100%; width: 100%;
margin: 0; margin: 0;
@ -61,20 +71,24 @@ form, input, label, p {
text-transform: uppercase; text-transform: uppercase;
font-weight: 700; font-weight: 700;
} }
.file-upload-btn:hover { .file-upload-btn:hover {
background: #2175f3; background: #2175f3;
color: #ffffff; color: #ffffff;
transition: all .2s ease; transition: all .2s ease;
cursor: pointer; cursor: pointer;
} }
.file-upload-btn:active { .file-upload-btn:active {
border: 0; border: 0;
transition: all .2s ease; transition: all .2s ease;
} }
.file-upload-content { .file-upload-content {
display: none; display: none;
text-align: center; text-align: center;
} }
.file-upload-input { .file-upload-input {
position: absolute; position: absolute;
margin: 0; margin: 0;
@ -85,34 +99,41 @@ form, input, label, p {
opacity: 0; opacity: 0;
cursor: pointer; cursor: pointer;
} }
.image-upload-wrap { .image-upload-wrap {
margin-top: 20px; margin-top: 20px;
border: 4px dashed #2196f3; border: 4px dashed #2196f3;
position: relative; position: relative;
} }
.image-dropping, .image-upload-wrap:hover { .image-dropping, .image-upload-wrap:hover {
background-color: #2196f3; background-color: #2196f3;
border: 4px dashed #ffffff; border: 4px dashed #ffffff;
} }
.image-title-wrap { .image-title-wrap {
padding: 0 15px 15px 15px; padding: 0 15px 15px 15px;
color: #222; color: #222;
} }
.drag-text { .drag-text {
text-align: center; text-align: center;
} }
.drag-text .buttonText { .drag-text .buttonText {
font-weight: 100; font-weight: 100;
text-transform: uppercase; text-transform: uppercase;
color: #fff; color: #fff;
padding: 60px 0; padding: 60px 0;
} }
.file-upload-image { .file-upload-image {
max-height: 200px; max-height: 200px;
max-width: 200px; max-width: 200px;
margin: auto; margin: auto;
padding: 20px; padding: 20px;
} }
.remove-image { .remove-image {
width: 200px; width: 200px;
margin: 0; margin: 0;
@ -127,12 +148,14 @@ form, input, label, p {
text-transform: uppercase; text-transform: uppercase;
font-weight: 700; font-weight: 700;
} }
.remove-image:hover { .remove-image:hover {
background: #c13b2a; background: #c13b2a;
color: #ffffff; color: #ffffff;
transition: all .2s ease; transition: all .2s ease;
cursor: pointer; cursor: pointer;
} }
.remove-image:active { .remove-image:active {
border: 0; border: 0;
transition: all .2s ease; transition: all .2s ease;

View File

@ -3,7 +3,8 @@
<div class="column shadow-sm p-3 m-4 bg-dark-3 text-light"> <div class="column shadow-sm p-3 m-4 bg-dark-3 text-light">
<div align="left"> <div align="left">
<h1>Upload</h1> <h1>Upload</h1>
<p>This page will allow you to upload photo spheres to Google Maps after the <i>idiots</i> at Google removed their perfectly working StreetView app.</p> <p>This page will allow you to upload photo spheres to Google Maps after the <i>idiots</i> at Google removed
their perfectly working StreetView app.</p>
<div class="alert alert-info" role="alert"> <div class="alert alert-info" role="alert">
<span>Images <b>must</b> have the proper 360 degree file properties, you will need to figure out how to add this metadata. <b>Google Pixel photosphere photos work perfectly</b>, I have yet to figure out how to get my DJI drone 360 photos working.</span> <span>Images <b>must</b> have the proper 360 degree file properties, you will need to figure out how to add this metadata. <b>Google Pixel photosphere photos work perfectly</b>, I have yet to figure out how to get my DJI drone 360 photos working.</span>
@ -17,10 +18,13 @@
<div class="form-group"> <div class="form-group">
<legend>Upload a 360 image</legend> <legend>Upload a 360 image</legend>
<div class="file-upload"> <div class="file-upload">
<button class="file-upload-btn" type="button" onclick="$('.file-upload-input').trigger('click')">Add Image</button> <button class="file-upload-btn" type="button" onclick="$('.file-upload-input').trigger('click')">Add
Image
</button>
<div class="image-upload-wrap"> <div class="image-upload-wrap">
<input class="file-upload-input" type='file' id="file" name="file" onchange="readURL(this);" accept="image/png, image/jpeg" /> <input class="file-upload-input" type='file' id="file" name="file" onchange="readURL(this);"
accept="image/png, image/jpeg"/>
<div class="drag-text"> <div class="drag-text">
<div class="buttonText"> <div class="buttonText">
@ -37,9 +41,10 @@
</div> </div>
</div> </div>
<div class="file-upload-content"> <div class="file-upload-content">
<img class="file-upload-image" src="" alt="Uploaded Image" /> <img class="file-upload-image" src="" alt="Uploaded Image"/>
<div class="image-title-wrap"> <div class="image-title-wrap">
<button type="button" onclick="removeUpload()" class="remove-image">Remove <span class="image-title">Uploaded Image</span></button> <button type="button" onclick="removeUpload()" class="remove-image">Remove <span
class="image-title">Uploaded Image</span></button>
</div> </div>
</div> </div>
</div> </div>
@ -51,11 +56,13 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lat" class="text-light">Latitude</label> <label for="lat" class="text-light">Latitude</label>
<input type="text" id="lat" name="lat" placeholder="Latitude coordinates" class="form-control" required /> <input type="text" id="lat" name="lat" placeholder="Latitude coordinates" class="form-control"
required/>
</div> </div>
<div class="form-control"> <div class="form-control">
<label for="long" class="text-light">Longitude</label> <label for="long" class="text-light">Longitude</label>
<input type="text" id="long" name="long" placeholder="Longitude coordinates" class="form-control" required /> <input type="text" id="long" name="long" placeholder="Longitude coordinates" class="form-control"
required/>
</div> </div>
<button type="submit" class="ripple">UPLOAD</button> <button type="submit" class="ripple">UPLOAD</button>

View File

@ -1,27 +1,28 @@
<p class="text-center text-light">Source Code: <a href="https://github.com/moom0o/PhotoSphereStudio/">Github</a> - Made
by <a href="https://github.com/moom0o">moom0o</a> & <a href="https://github.com/WinsDominoes">Win</a></p>
</div>
<script src="/assets/drag-and-drop.js"></script>
<p class="text-center text-light">Source Code: <a href="https://github.com/moom0o/PhotoSphereStudio/">Github</a> - Made by <a href="https://github.com/moom0o">moom0o</a> & <a href="https://github.com/WinsDominoes">Win</a></p> <!-- LEAFLET JS -->
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"
integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.js" charset="utf-8"></script>
<script src="/assets/leaflet/js/map.js"></script>
</div> <!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="/assets/drag-and-drop.js"></script> <!-- EXIF JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js"
integrity="sha512-bE9sOOprru4D2nRKDQSNvhdxw47Jd0r2s7QThKB/KkU5HEPuxsk9DbK+fYIMCYlCsXkwp6M87vj84sAtYWqxvw=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- LEAFLET JS --> <!-- Then Material JavaScript on top of Bootstrap JavaScript -->
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js" integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script> <script src="/assets/material/material.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.js" charset="utf-8"></script>
<script src="/assets/leaflet/js/map.js"></script>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<!-- EXIF JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js" integrity="sha512-bE9sOOprru4D2nRKDQSNvhdxw47Jd0r2s7QThKB/KkU5HEPuxsk9DbK+fYIMCYlCsXkwp6M87vj84sAtYWqxvw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- Then Material JavaScript on top of Bootstrap JavaScript -->
<script src="/assets/material/material.min.js"></script>
</body> </body>
</html> </html>

View File

@ -9,23 +9,26 @@
<link rel="stylesheet" href="/assets/style.css"> <link rel="stylesheet" href="/assets/style.css">
<!-- Add Material font (Roboto) and Material icon as needed --> <!-- Add Material font (Roboto) and Material icon as needed -->
<link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i,700,700i|Roboto+Mono:300,400,700|Roboto+Slab:300,400,700" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i,700,700i|Roboto+Mono:300,400,700|Roboto+Slab:300,400,700"
rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Add Material CSS, replace Bootstrap CSS --> <!-- Add Material CSS, replace Bootstrap CSS -->
<link href="/assets/material/material.min.css" rel="stylesheet"> <link href="/assets/material/material.min.css" rel="stylesheet">
<!-- GOOGLE ICONS --> <!-- GOOGLE ICONS -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" /> <link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0"/>
<!-- LEAFLET FOR MAP --> <!-- LEAFLET FOR MAP -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin="" /> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css"
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.css" /> integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin=""/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.css"/>
<title>PhotoSphereStudio - Upload 360° Photos to Google Maps</title> <title>PhotoSphereStudio - Upload 360° Photos to Google Maps</title>
</head> </head>
<body class="bg-dark-2"> <body class="bg-dark-2">
<header> <header>
<nav class="navbar navbar-expand-lg navbar-dark bg-info"> <nav class="navbar navbar-expand-lg navbar-dark bg-info">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
@ -33,10 +36,11 @@
<img src="/assets/icons/icon.png" alt="Icon" id="icon"> <img src="/assets/icons/icon.png" alt="Icon" id="icon">
PhotoSphereStudio PhotoSphereStudio
</span></a> </span></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
</nav> </nav>
</header> </header>
<div align="center"> <div align="center">