diff --git a/.gitignore b/.gitignore
index b6cfbbf..11b0027 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,3 +81,8 @@ fabric.properties
# .NET
obj/
bin/
+
+# Ignore user uploads
+uploads/
+
+__pycache__
\ No newline at end of file
diff --git a/sample.env b/sample.env
index 4a396e3..e2c1fff 100644
--- a/sample.env
+++ b/sample.env
@@ -25,6 +25,12 @@ LOGIN_SERVER_UID=""
#Login server secret
LOGIN_SERVER_SECRET=""
+#=====POSTGRES=====
+POSTGRES_HOST = ""
+POSTGRES_DBNAME = ""
+POSTGRES_USER = ""
+POSTGRES_PASS = ""
+POSTGRES_PORT =
#=====General=====
#The domain on which stickermap will be hosted (without port)
@@ -32,10 +38,10 @@ STICKER_MAP_URL="http://localhost"
#The port on which stickermap will be hosted.
STICKER_MAP_PORT=7050
#The color of all the UI elements on the site (make sure that white text is readable on this color)
-STICKER_MAP_COLOR="#29738f"
+STICKER_MAP_COLOR="#197052"
#Determine if the map is viewable without loging in
STICKER_MAP_REQUIRE_LOGIN=False
#How long should an admin session be valid? (seconds)
ADMIN_EXPIRES_IN=1800
-PGDATABASE = "sticker"
+PGDATABASE = "sticker"
\ No newline at end of file
diff --git a/server.py b/server.py
index 142bb79..2890799 100644
--- a/server.py
+++ b/server.py
@@ -5,11 +5,16 @@
import psycopg2
import time
import json
+from decimal import Decimal
import os
from werkzeug.utils import redirect, secure_filename
import random
from dotenv import load_dotenv
import secrets
+import datetime
+
+if (not os.path.exists("./static/uploads")):
+ os.mkdir("./static/uploads")
# Load env file for variable
load_dotenv()
@@ -17,12 +22,30 @@
# Create flask app
app = flask.Flask(__name__)
+POSTGRES_HOST = os.getenv("POSTGRES_HOST")
+POSTGRES_DBNAME = os.getenv("POSTGRES_DBNAME")
+POSTGRES_USER = os.getenv("POSTGRES_USER")
+POSTGRES_PASS = os.getenv("POSTGRES_PASS")
+POSTGRES_PORT = os.getenv("POSTGRES_PORT")
BOARD_COLOR = os.getenv("STICKER_MAP_COLOR")
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
UPLOAD_DIRECTORY = "static/uploads"
+con = psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT)
+
+cursor = con.cursor()
+
+cursor.execute("CREATE TABLE IF NOT EXISTS stickers (stickerID SERIAL PRIMARY KEY, stickerLat Decimal(8,6), stickerLon Decimal(9,6), logoID INT, pictureUrl VARCHAR(255), adderEmail VARCHAR(255), postTime TIMESTAMP, spots INT, verified INT)")
+# cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail, verified) VALUES (52.1630587, 5.402001, 1, 'zwart 1920x1080.png', 'aron.den.ouden@gmail.com', 1)")
+
+con.commit()
+
+cursor.close()
+con.close()
+
+
@app.route('/')
def stickerMap():
if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True":
@@ -72,7 +95,7 @@ def auth():
if 'credentials_type' not in tokenResponse:
return redirect('/auth', code=302)
# Connect to db
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
cursor = con.cursor()
# Check if the user already has (normal) key stored. (Admin refresh session)
token = ""
@@ -121,10 +144,15 @@ def uploadSticker():
# Save file
file.save(os.path.join(UPLOAD_DIRECTORY, filename))
# create db entry
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
emailCode = random.randrange(9999999, 999999999)
+
+ sampleEmail = "joe@joecompany.com"
+ currentTime = str(datetime.datetime.now())
+
cursor = con.cursor()
- cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail) VALUES (%s,%s,%s,%s,%s)", (request.form['lat'], request.form['lon'], request.form['logoId'], os.path.join(UPLOAD_DIRECTORY, filename), emailCode))
+ # cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail) VALUES (%s,%s,%s,%s,%s)", (request.form['lat'], request.form['lon'], request.form['logoId'], os.path.join(UPLOAD_DIRECTORY, filename), emailCode))
+ cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail, postTime, spots, verified) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", (request.form['lat'], request.form['lon'], 1, os.path.join(UPLOAD_DIRECTORY, filename), emailCode, currentTime, 0, 1))
con.commit()
return json.dumps({'status': '200', 'error': 'Sticker added to database.', 'emailCode': emailCode}), 200
else:
@@ -140,13 +168,13 @@ def uploadSticker():
@app.route('/logos', methods=['GET'])
def getLogos():
# Check token if required
- if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True":
- if not checkToken(request.args.get('token')):
+ # if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True":
+ # if not checkToken(request.args.get('token')):
return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405
- with psycopg2.connect() as con:
- cursor = con.cursor()
- results = cursor.execute('SELECT * FROM logos ORDER BY logoTitle DESC').fetchall()
- return json.dumps(results)
+ # with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
+ # cursor = con.cursor()
+ # results = cursor.execute('SELECT * FROM logos ORDER BY logoTitle DESC').fetchall()
+ # return json.dumps(results)
@app.route('/editLogo', methods=['PATCH'])
@@ -156,7 +184,7 @@ def editLogo():
return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403
if request.args.get("id") == None or request.args.get('name') == None or request.args.get('color') == None:
return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}), 400
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
cursor = con.cursor();
cursor.execute('UPDATE logos SET logoTitle=%s, logoColor=%s WHERE logoId=%s', (request.args.get('name'), request.args.get('color'), request.args.get('id')))
con.commit()
@@ -170,7 +198,7 @@ def deleteLogo():
return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403
if request.args.get("id") is None:
return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}), 400
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
cursor = con.cursor()
cursor.execute('DELETE FROM logos WHERE logoId=%s', (request.args.get('id'),))
con.commit()
@@ -184,7 +212,7 @@ def addLogo():
return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403
if request.args.get('name') == None or request.args.get('color') == None:
return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}), 400
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
cursor = con.cursor()
cursor.execute('INSERT INTO logos (logoTitle, logoColor) VALUES (%s,%s)', (request.args.get('name'), request.args.get('color')))
con.commit()
@@ -200,7 +228,7 @@ def addEmail():
if request.form['email'] != '':
if request.form['token'] != '':
# Check if the token is in the database
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
cursor = con.cursor()
result = cursor.execute('SELECT * FROM stickers WHERE adderEmail=%s', (request.form['token'],)).fetchall()
if len(result) != 0:
@@ -224,12 +252,49 @@ def getStickers():
return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405
if (request.args.get('west') != '' and request.args.get('east') != '' and request.args.get('north') != '' and request.args.get('south') != ''):
# Get all the stickers within the bounding box
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
+ # create cursor
+ cursor = con.cursor()
+ # find results
+ cursor.execute("SELECT * FROM stickers WHERE stickerLat BETWEEN %s AND %s AND stickerLon BETWEEN %s AND %s", (request.args.get('south'), request.args.get('north'), request.args.get('west'), request.args.get('east')))
+
+ rows = cursor.fetchall()
+
+ return json.dumps(rows, default=str)
+ else:
+ return json.dumps({'status': '400', 'error': 'Bounding box not defined or incomplete.'}), 400
+
+@app.route('/getNearYouStickers', methods=['GET'])
+def getNearYouStickers():
+ # Check token if required
+ if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True":
+ if not checkToken(request.cookies.get('token')):
+ return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405
+ if (request.args.get('lon') != '' and request.args.get('lat') != ''):
+ # Get all the stickers within the bounding box
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
# create cursor
cursor = con.cursor()
# find results
- rows = cursor.execute("SELECT * FROM stickers WHERE stickerLat BETWEEN %s AND %s AND stickerLon BETWEEN %s AND %s AND verified='1'", (request.args.get('south'), request.args.get('north'), request.args.get('west'), request.args.get('east'))).fetchall()
- return json.dumps(rows)
+ # cursor.execute("""SELECT *, ST_DistanceSphere(
+ # ST_MakePoint(5.172445, 52.086240),
+ # ST_MakePoint(stickerLon, stickerLat)
+ # ) AS distance
+ # FROM stickers
+ # ORDER BY distance ASC
+ # LIMIT 10""")
+
+ cursor.execute("""SELECT *, ST_DistanceSphere(
+ ST_MakePoint(%s, %s),
+ ST_MakePoint(stickerLon, stickerLat)
+ ) AS distance
+ FROM stickers
+ ORDER BY distance ASC
+ LIMIT 10""", (float(request.args.get('lon')), float(request.args.get('lat'))))
+
+ rows = cursor.fetchall()
+
+ return json.dumps(rows, default=str)
else:
return json.dumps({'status': '400', 'error': 'Bounding box not defined or incomplete.'}), 400
@@ -240,7 +305,7 @@ def getUnverifiedStickers():
if not checkAdminToken(request.cookies.get('adminToken')):
return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403
# Get all unverified stickers'
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
# create cursor
cursor = con.cursor()
# find results
@@ -255,7 +320,7 @@ def setSticker():
return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403
if request.args.get("id") == None or request.args.get('state') == None:
return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'})
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
# create a cursor
cursor = con.cursor()
# Get the email address of the user
@@ -274,6 +339,30 @@ def setSticker():
return json.dumps({'status': '400', 'error': 'Invalid card state'})
+@app.route('/updateStickerSpots', methods=['POST'])
+def updateStickerSpots():
+ # Check token if required
+ if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True":
+ if not checkToken(request.cookies.get('token')):
+ return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405
+
+ data = request.get_json()
+ stickerID = data.get('stickerID')
+
+ if (stickerID != ''):
+ # Get all the stickers within the bounding box
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
+ # create cursor
+ cursor = con.cursor()
+ # Increase spot count
+ cursor.execute("UPDATE stickers SET spots = spots + 1 WHERE stickerID = %s", (stickerID,))
+
+ return json.dumps({'status': '200', 'error': 'Updated spots count'}), 200
+ else:
+ return json.dumps({'status': '400', 'error': 'Updating sticker spots failed'}), 400
+
+
+
def sendEmailUpdate():
return 0 # TODO not implemented
@@ -282,7 +371,7 @@ def checkToken(token):
# print(token)
if token is None:
return False
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
cursor = con.cursor()
cursor.execute("SELECT * FROM tokens WHERE token=%s", (token,))
rows = cursor.fetchall()
@@ -297,7 +386,7 @@ def checkAdminToken(token):
if token is None:
return False
# Connect with database
- with psycopg2.connect() as con:
+ with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con:
# Remove invalid keys from the database
cursor = con.cursor()
cursor.execute("DELETE FROM adminTokens WHERE %s > expirationTime", (time.time(),))
diff --git a/static/admin.css b/static/css/admin.css
similarity index 94%
rename from static/admin.css
rename to static/css/admin.css
index 0c1f63a..f733b17 100644
--- a/static/admin.css
+++ b/static/css/admin.css
@@ -1,286 +1,286 @@
-*{
- margin: 0;
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-}
-
-
-.content{
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 100%;
- overflow: hidden;
-}
-
-.cardSection{
- width: 100%;
- max-width: 400px;
- position: relative;
- padding-top: 10px;
- margin: 10px 0px 10px 0px;
- border-radius: 15px;
- background-color: rgb(30,30,30);
-}
-
-.navBar{
- width: 100%;
- height: 80px;
- display: flex;
- align-items: center;
- flex-direction: row;
- background-color: rgb(30,30,30);
- box-shadow: 0px 3px 5px rgba(0,0,0,0.5);
-}
-
-.title{
- color: white;
- font-size: 30px;
- margin-left: 30px;
-}
-
-.containerLabel{
- color: white;
- display: block;
- font-size: 25px;
- margin-left: 35px;
-}
-
-.cardHolder{
- height: 450px;
-}
-
-.card{
- position: absolute;
- overflow: hidden;
- margin: 15px 30px 30px 30px;
- border-radius: 20px;
- box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
- width: calc(100% - 60px);
- /*animation: cardSwipeRight 1s infinite;
- animation-direction: alternate-reverse; */
- transform-origin: bottom;
- touch-action: none;
-}
-
-@keyframes cardSwipeLeft {
- 0%{
- transform: translate(0px, 0px) rotate(0deg);
- opacity: 1;
- }
- 100%{
- transform: translate(-200px, 30px) rotate(-30deg);
- opacity: 0;
- }
-}
-
-@keyframes cardSwipeRight {
- 0%{
- transform: translate(0px, 0px) rotate(0deg);
- opacity: 1;
- }
- 100%{
- transform: translate(200px, 30px) rotate(30deg);
- opacity: 0;
- }
-}
-
-.stickerImage{
- width: 100%;
- height: 350px;
- object-fit: cover;
-}
-
-.buttonArea{
- position: relative;
- height: 80px;
- background: linear-gradient(rgba(255,255,255, 0) 0%, rgba(255,255,255,1) 50%);
- margin-top: -45px;
- display: flex;
- justify-content: space-around;
-}
-
-.cardButton{
- width: 80px;
- height: 80px;
- margin-top: -25px;
- background-size:cover;
-
-}
-
-.accept{
- background-image: url('/static/thumb-up.svg');
-}
-
-.reject{
- background-image: url('/static/thumb-down.svg');
-
-}
-
-.bottomSection{
- width: 100%;
- display: flex;
- flex-direction: row;
- color: white;
-}
-
-@media screen and (max-width:700px) {
- .bottomSection{
- flex-direction: column;
- }
- .allStickersSection{
- margin: 0px 0px 10px 0px !important;
- width: calc(100% - 20px) !important;
- }
- .allLogosSection{
- margin: 0px !important;
- width: calc(100% - 20px) !important;
- }
-}
-
-.containerDivider{
- margin: 5px 20px 10px 20px;
- border-top: rgb(150,150,150) solid 1px;
- border-bottom: none;
-}
-
-.allStickersSection{
- background-color: rgb(30,30,30);
- width: 50%;
- margin: 0px 5px 0px 10px;
- padding: 10px;
- border-radius: 15px;
-}
-
-.allLogosSection{
- background-color: rgb(30,30,30);
- width: 50%;
- margin: 0px 10px 0px 5px;
- padding: 10px;
- border-radius: 15px;
-}
-
-.containerGrid{
- margin: 0px 20px 0px 20px;
- width: calc(100% - 40px);
- display: flex;
- flex-wrap: wrap;
- justify-content: flex-start;
-}
-
-.gridItem{
- width: 130px;
- height: 150px;
- display: flex;
- justify-content: center;
- align-items: center;
- border:rgb(112, 112, 112) solid;
- margin-right: 3px;
-}
-
-.gridItem:hover{
- background-color: rgb(71, 67, 67);
-}
-
-.gridImage{
- width: 100px;
-}
-
-.logoContainer{
- margin-left: 20px;
- margin-right: 20px;
-}
-
-.logoRow{
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 10px;
-}
-
-.logoIcon{
- width: 40px;
- height: 40px;
-}
-
-.logoLabel{
- margin-left: 20px;
- font-size: 18px;
-}
-
-.logoControls{
- margin-right: 0;
- margin-left: auto;
-}
-
-.logoControl{
- margin-left: 10px;
-}
-
-.logoAddButton{
- width: 100%;
- height: 40px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: bold;
- font-size: 20px;
- cursor: pointer;
-}
-
-.logoEditBackdrop{
- position: fixed;
- top: 0px;
- width: 100%;
- height: 100%;
- background-color: rgba(0,0,0,0.8);
- display: none;
- align-items: center;
- justify-content: center;
- opacity: 0;
- transition: opacity 0.5s;
-}
-
-.logoEdit{
- background-color: white;
- display: flex;
- flex-direction: column;
- width: 80%;
- max-width: 300px;
- border-radius: 10px;
- overflow: hidden;
-}
-
-.logoEditTitle{
- margin: 15px;
- font-size: 25px;
- text-align: center;
- font-weight: bold;
-}
-
-.logoEditLabel{
- margin-left: 10px;
- margin-top: 10px;
-}
-
-.logoEditInput{
- margin-left: 10px;
- margin-right: 10px;
- height: 30px;
- font-size: 20px;
- border: none;
- outline: none;
- border-bottom: solid 0.5px rgb(0,0,0,0.5);
-}
-
-.logoEditButton{
- display: flex;
- justify-content: center;
- margin-top: 20px;
- height: 40px;
- color: white;
- font-size: 20px;
- font-weight: bold;
- align-items: center;
- background-color: rgb(0, 140, 7);
- cursor: pointer;
+*{
+ margin: 0;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+
+.content{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ overflow: hidden;
+}
+
+.cardSection{
+ width: 100%;
+ max-width: 400px;
+ position: relative;
+ padding-top: 10px;
+ margin: 10px 0px 10px 0px;
+ border-radius: 15px;
+ background-color: rgb(30,30,30);
+}
+
+.navBar{
+ width: 100%;
+ height: 80px;
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ background-color: rgb(30,30,30);
+ box-shadow: 0px 3px 5px rgba(0,0,0,0.5);
+}
+
+.title{
+ color: white;
+ font-size: 30px;
+ margin-left: 30px;
+}
+
+.containerLabel{
+ color: white;
+ display: block;
+ font-size: 25px;
+ margin-left: 35px;
+}
+
+.cardHolder{
+ height: 450px;
+}
+
+.card{
+ position: absolute;
+ overflow: hidden;
+ margin: 15px 30px 30px 30px;
+ border-radius: 20px;
+ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
+ width: calc(100% - 60px);
+ /*animation: cardSwipeRight 1s infinite;
+ animation-direction: alternate-reverse; */
+ transform-origin: bottom;
+ touch-action: none;
+}
+
+@keyframes cardSwipeLeft {
+ 0%{
+ transform: translate(0px, 0px) rotate(0deg);
+ opacity: 1;
+ }
+ 100%{
+ transform: translate(-200px, 30px) rotate(-30deg);
+ opacity: 0;
+ }
+}
+
+@keyframes cardSwipeRight {
+ 0%{
+ transform: translate(0px, 0px) rotate(0deg);
+ opacity: 1;
+ }
+ 100%{
+ transform: translate(200px, 30px) rotate(30deg);
+ opacity: 0;
+ }
+}
+
+.stickerImage{
+ width: 100%;
+ height: 350px;
+ object-fit: cover;
+}
+
+.buttonArea{
+ position: relative;
+ height: 80px;
+ background: linear-gradient(rgba(255,255,255, 0) 0%, rgba(255,255,255,1) 50%);
+ margin-top: -45px;
+ display: flex;
+ justify-content: space-around;
+}
+
+.cardButton{
+ width: 80px;
+ height: 80px;
+ margin-top: -25px;
+ background-size:cover;
+
+}
+
+.accept{
+ background-image: url('/static/thumb-up.svg');
+}
+
+.reject{
+ background-image: url('/static/thumb-down.svg');
+
+}
+
+.bottomSection{
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ color: white;
+}
+
+@media screen and (max-width:700px) {
+ .bottomSection{
+ flex-direction: column;
+ }
+ .allStickersSection{
+ margin: 0px 0px 10px 0px !important;
+ width: calc(100% - 20px) !important;
+ }
+ .allLogosSection{
+ margin: 0px !important;
+ width: calc(100% - 20px) !important;
+ }
+}
+
+.containerDivider{
+ margin: 5px 20px 10px 20px;
+ border-top: rgb(150,150,150) solid 1px;
+ border-bottom: none;
+}
+
+.allStickersSection{
+ background-color: rgb(30,30,30);
+ width: 50%;
+ margin: 0px 5px 0px 10px;
+ padding: 10px;
+ border-radius: 15px;
+}
+
+.allLogosSection{
+ background-color: rgb(30,30,30);
+ width: 50%;
+ margin: 0px 10px 0px 5px;
+ padding: 10px;
+ border-radius: 15px;
+}
+
+.containerGrid{
+ margin: 0px 20px 0px 20px;
+ width: calc(100% - 40px);
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+}
+
+.gridItem{
+ width: 130px;
+ height: 150px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border:rgb(112, 112, 112) solid;
+ margin-right: 3px;
+}
+
+.gridItem:hover{
+ background-color: rgb(71, 67, 67);
+}
+
+.gridImage{
+ width: 100px;
+}
+
+.logoContainer{
+ margin-left: 20px;
+ margin-right: 20px;
+}
+
+.logoRow{
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.logoIcon{
+ width: 40px;
+ height: 40px;
+}
+
+.logoLabel{
+ margin-left: 20px;
+ font-size: 18px;
+}
+
+.logoControls{
+ margin-right: 0;
+ margin-left: auto;
+}
+
+.logoControl{
+ margin-left: 10px;
+}
+
+.logoAddButton{
+ width: 100%;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+ font-size: 20px;
+ cursor: pointer;
+}
+
+.logoEditBackdrop{
+ position: fixed;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.8);
+ display: none;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.5s;
+}
+
+.logoEdit{
+ background-color: white;
+ display: flex;
+ flex-direction: column;
+ width: 80%;
+ max-width: 300px;
+ border-radius: 10px;
+ overflow: hidden;
+}
+
+.logoEditTitle{
+ margin: 15px;
+ font-size: 25px;
+ text-align: center;
+ font-weight: bold;
+}
+
+.logoEditLabel{
+ margin-left: 10px;
+ margin-top: 10px;
+}
+
+.logoEditInput{
+ margin-left: 10px;
+ margin-right: 10px;
+ height: 30px;
+ font-size: 20px;
+ border: none;
+ outline: none;
+ border-bottom: solid 0.5px rgb(0,0,0,0.5);
+}
+
+.logoEditButton{
+ display: flex;
+ justify-content: center;
+ margin-top: 20px;
+ height: 40px;
+ color: white;
+ font-size: 20px;
+ font-weight: bold;
+ align-items: center;
+ background-color: rgb(0, 140, 7);
+ cursor: pointer;
}
\ No newline at end of file
diff --git a/static/auth.css b/static/css/auth.css
similarity index 100%
rename from static/auth.css
rename to static/css/auth.css
diff --git a/static/home.css b/static/css/home.css
similarity index 58%
rename from static/home.css
rename to static/css/home.css
index 8bd86d4..f149419 100644
--- a/static/home.css
+++ b/static/css/home.css
@@ -1,263 +1,393 @@
-*{
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-}
-
-body{
- margin: 0px;
- height: 100%;
-}
-
-.navBar{
- position: relative;
- height: 80px;
- display: flex;
- flex-direction: row;
- align-items: center;
- z-index: 5;
- box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.5);
-}
-
-.addIcon{
- margin-right: 20px;
- margin-left: auto;
- transform: rotate(0deg);
- transition: transform 0.2s;
-}
-
-.addIconClose{
- transform: rotate(45deg);
-}
-
-#map{
- height: calc(100% - 80px);
- width: 100%;
- z-index: 1;
-}
-
-.addContainer{
- width: 100%;
- height: 100%;
- z-index: 2;
- display: flex;
- position: fixed;
- flex-direction: column;
- align-items: center;
-}
-
-.addView{
- background-color: white;
- position: fixed;
- z-index: 2;
- top: -500px;
- width: 100%;
- max-width: 500px;
- display: flex;
- flex-direction: column;
- transition: top 0.5s;
- border-radius: 0px 0px 20px 20px;
-}
-
-.openView{
- top: 80px !important;
-}
-
-.addViewTitle{
- font-size: 30px;
- font-weight: bold;
- padding-top: 10px;
- padding-left: 20px;
-}
-
-.locationContainer{
- background-color: rgba(0, 0, 0, 0.1);
- margin-left: 20px;
- margin-right: 20px;
- margin-top: 15px;
- border-radius: 15px;
-
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.locationContainerSpinner{
- margin: 10px;
- min-width: 40px;
- display: none;
-}
-
-/* location icon */
-@keyframes outerAnim {
- from{
- transform: scale(1);
- opacity: 1;
- }
- to{
- transform: scale(2.5);
- opacity: 0;
- }
-}
-
-.locationIcon{
- margin: 10px;
- min-width: 40px;
-}
-
-.outerCircle{
- animation: outerAnim 1s infinite;
- transform-origin: center;
-}
-
-.locationContainerText{
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 15px;
- color: rgb(100,100,100);
- cursor: pointer;
-}
-
-.addInputDes{
- margin-left: 20px;
- margin-top: 5px;
-}
-
-.addInput{
- margin-left: 20px;
- margin-right: 20px;
- margin-top: 3px;
-}
-
-.addInput{
- display: none;
-}
-
-.locationInputDes{
- display: none;
-}
-
-.addImageInput{
- margin-left: 20px;
- margin-top: 20px;
- width: calc(100% - 48px);
- height: 200px;
-
- display: flex;
- align-items: center;
- justify-content: center;
-
- border: 4px dashed rgba(0, 0, 0, 0.3);
- background-color: white;
- color: grey;
- cursor: pointer;
- transition: background-color 0.2s, color 0.2s, border 0.2s;
-}
-
-.highlight{
- background-color: rgba(0, 0, 0, 0.6);
- border: 4px solid rgba(255, 255, 255, 0.3);
- color: white;
-}
-
-.imageError{
- background-color: rgba(255, 0, 0, 0.6);
- border: 4px solid rgba(255, 255, 255, 0.3);
- color: white;
-}
-
-.addImageTextHidden{
- display: none;
-}
-
-.addImagePreview{
- display: none;
- width: 100%;
- height: 100%;
- object-fit: cover;
-}
-.addImagePreviewShow{
- display: block !important;
-}
-
-.addSubmitButton{
- margin-top: 10px;
- width: 100%;
- height: 40px;
- align-items: center;
- justify-content: center;
- border-radius: 0px 0px 20px 20px;
- display: flex;
- color: white;
- font-weight: bold;
- cursor: pointer;
-}
-
-.addLogoSelector{
- display: flex;
- flex-direction: row;
-
- margin-left: 20px;
- margin-top: 10px;
- margin-right: 20px;
- justify-content: center;
-}
-
-.logoELement{
- margin-right: 5px;
- padding: 5px;
- border-radius: 5px;
- transition: background-color 0.2s;
-}
-
-.logoElementSelected{
- background-color: rgba(155, 155, 155, 0.329);
-}
-
-.addSubmitButtonPressed{
- animation: submitPressed 2s infinite;
-}
-
-.successView{
- background-color: white;
- position: fixed;
- z-index: 2;
- top: -300px;
- width: 100%;
- max-width: 500px;
- display: flex;
- flex-direction: column;
- transition: top 0.5s;
- border-radius: 0px 0px 20px 20px;
-}
-
-.successText{
- margin-top: 10px;
- margin-left: 22px;
- margin-right: 22px;
- font-size: 14px;
- color:rgb(49, 49, 49)
-}
-
-.successEmail{
- margin-top: 15px;
- margin-left: 20px;
- margin-right: 20px;
- margin-bottom: 10px;
-
- height: 40px;
- font-size: 18px;
- padding-left: 10px;
- border-radius: 10px;
- border: solid 1px rgba(200,200,200);
- outline: none;
- transition: border-color 0.2s;
-}
-
-.invalid{
- border-color: rgb(248, 50, 50);
-}
-
-.customDivIcon{
- background-color: white;
- border-radius: 50%;
- padding: 4px;
-}
\ No newline at end of file
+*{
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ overflow: hidden;
+ overscroll-behavior: none;
+}
+
+body{
+ /* margin: 0px; */
+ height: 100%;
+}
+
+.navBar{
+ position: relative;
+ /* height: 80px; */
+ height: 10vh;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ z-index: 5;
+ box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.5);
+}
+
+.addIcon{
+ margin-right: 20px;
+ margin-left: auto;
+ transform: rotate(0deg);
+ transition: transform 0.2s;
+}
+
+.addIconClose{
+ transform: rotate(45deg);
+}
+
+#map{
+ height: calc(100% - 80px);
+ width: 100%;
+ z-index: 1;
+}
+
+.addContainer{
+ width: 100%;
+ height: 100%;
+ z-index: 2;
+ display: flex;
+ position: fixed;
+ flex-direction: column;
+ align-items: center;
+}
+
+.addView{
+ background-color: white;
+ position: fixed;
+ z-index: 2;
+ top: -500px;
+ width: 100%;
+ max-width: 500px;
+ display: flex;
+ flex-direction: column;
+ transition: top 0.5s;
+ border-radius: 0px 0px 20px 20px;
+}
+
+.openView{
+ top: 80px !important;
+}
+
+.addViewTitle{
+ font-size: 30px;
+ font-weight: bold;
+ padding-top: 10px;
+ padding-left: 20px;
+}
+
+.locationContainer{
+ background-color: rgba(0, 0, 0, 0.1);
+ margin-left: 20px;
+ margin-right: 20px;
+ margin-top: 15px;
+ border-radius: 15px;
+
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.locationContainerSpinner{
+ margin: 10px;
+ min-width: 40px;
+ display: none;
+}
+
+/* location icon */
+@keyframes outerAnim {
+ from{
+ transform: scale(1);
+ opacity: 1;
+ }
+ to{
+ transform: scale(2.5);
+ opacity: 0;
+ }
+}
+
+.locationIcon{
+ margin: 10px;
+ min-width: 40px;
+}
+
+.outerCircle{
+ animation: outerAnim 1s infinite;
+ transform-origin: center;
+}
+
+.locationContainerText{
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 15px;
+ color: rgb(100,100,100);
+ cursor: pointer;
+}
+
+.addInputDes{
+ margin-left: 20px;
+ margin-top: 5px;
+}
+
+.addInput{
+ margin-left: 20px;
+ margin-right: 20px;
+ margin-top: 3px;
+}
+
+.addInput{
+ display: none;
+}
+
+.locationInputDes{
+ display: none;
+}
+
+.addImageInput{
+ margin-left: 20px;
+ margin-top: 20px;
+ width: calc(100% - 48px);
+ height: 200px;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ border: 4px dashed rgba(0, 0, 0, 0.3);
+ background-color: white;
+ color: grey;
+ cursor: pointer;
+ transition: background-color 0.2s, color 0.2s, border 0.2s;
+}
+
+.highlight{
+ background-color: rgba(0, 0, 0, 0.6);
+ border: 4px solid rgba(255, 255, 255, 0.3);
+ color: white;
+}
+
+.imageError{
+ background-color: rgba(255, 0, 0, 0.6);
+ border: 4px solid rgba(255, 255, 255, 0.3);
+ color: white;
+}
+
+.addImageTextHidden{
+ display: none;
+}
+
+.addImagePreview{
+ display: none;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+.addImagePreviewShow{
+ display: block !important;
+}
+
+.addSubmitButton{
+ margin-top: 10px;
+ width: 100%;
+ height: 40px;
+ align-items: center;
+ justify-content: center;
+ border-radius: 0px 0px 20px 20px;
+ display: flex;
+ color: white;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+.addLogoSelector{
+ display: flex;
+ flex-direction: row;
+
+ margin-left: 20px;
+ margin-top: 10px;
+ margin-right: 20px;
+ justify-content: center;
+}
+
+.logoELement{
+ margin-right: 5px;
+ padding: 5px;
+ border-radius: 5px;
+ transition: background-color 0.2s;
+}
+
+.logoElementSelected{
+ background-color: rgba(155, 155, 155, 0.329);
+}
+
+.addSubmitButtonPressed{
+ animation: submitPressed 2s infinite;
+}
+
+.successView{
+ background-color: white;
+ position: fixed;
+ z-index: 2;
+ top: -300px;
+ width: 100%;
+ max-width: 500px;
+ display: flex;
+ flex-direction: column;
+ transition: top 0.5s;
+ border-radius: 0px 0px 20px 20px;
+}
+
+.successText{
+ margin-top: 10px;
+ margin-left: 22px;
+ margin-right: 22px;
+ font-size: 14px;
+ color:rgb(49, 49, 49)
+}
+
+.successEmail{
+ margin-top: 15px;
+ margin-left: 20px;
+ margin-right: 20px;
+ margin-bottom: 10px;
+
+ height: 40px;
+ font-size: 18px;
+ padding-left: 10px;
+ border-radius: 10px;
+ border: solid 1px rgba(200,200,200);
+ outline: none;
+ transition: border-color 0.2s;
+}
+
+.invalid{
+ border-color: rgb(248, 50, 50);
+}
+
+.customDivIcon{
+ background-color: white;
+ border-radius: 50%;
+ padding: 4px;
+}
+
+
+/* leaflet popup styling */
+.leaflet-popup-content-wrapper .leaflet-popup-content {
+ display: flex;
+ flex-direction: column;
+ /* justify-content: center;
+ align-items: center; */
+}
+
+.leaflet-popup-content-wrapper .leaflet-popup-content h1 {
+ text-align: center;
+ margin-bottom: 1rem;
+}
+
+.leaflet-popup-content-wrapper .leaflet-popup-content img {
+ border-radius: 5px;
+}
+
+.leafletMarkerButton {
+ background-color:#61518f;
+ color:#ffffff;
+ border: none;
+ border-radius: 5px;
+ padding: 0.5rem 0.5rem;
+ margin-top: 1rem;
+ margin-left: auto;
+ margin-right: auto;
+ width: 80%;
+}
+
+/* Maybe add styling for when you hover or click a button? */
+.leafletMarkerButton:hover {
+ /* background-color:#be0202; */
+}
+
+/* nearYouMobileOverlay */
+#nearYouMobileOverlay {
+ position: fixed;
+ z-index: 999;
+ bottom: -90%; /* Initially at 10% visible */
+ left: 0;
+ width: 100%;
+ height: 100%; /* Full screen height */
+ background-color: rgb(255, 255, 255);
+ /* border: 5px solid #61518f; */
+ transition: bottom 0.5s, border-radius 0.5s; /* Transition for sliding */
+ transition-timing-function: 'ease-in';
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ /* Enable scrolling if content exceeds screen height */
+ border-radius: 30px 30px 0px 0px;
+ box-shadow: 0px -3px 5px rgba(0, 0, 0, 0.5);
+}
+
+#nearYouMobileLine {
+ margin-top: 0.5rem;
+ width: 10%;
+}
+
+#nearYouMobileTopText {
+ margin-top: 0.5rem;
+}
+
+#nearYouMobileP {
+ padding: 0 2rem;
+ opacity: 0;
+ transition: opacity 0.8s;
+ transition-timing-function: 'ease-in';
+}
+
+/* sticker divs styling */
+.stickerDiv {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color: #ffffff;
+ transition: background-color 0.8s;
+ transition-timing-function: 'ease-in';
+ margin-bottom: 2rem;
+ width: 80%;
+ border-radius: 5px;
+}
+
+.stickerDiv h3 {
+ font-weight: 600;
+}
+
+.stickerDiv .stickerDivImg {
+ width: 40%;
+ border-radius: 5px;
+}
+
+.stickerDiv .stickerDivButton {
+ background-color:#61518f;
+ color:#ffffff;
+ border: none;
+ border-radius: 5px;
+ padding: 0.5rem 0.5rem;
+ margin-left: auto;
+ margin-right: auto;
+ margin-bottom: 0.5rem;
+ width: 30%;
+}
+
+.stickerDiv.revealed {
+ background-color: #e0e0e0;
+}
+
+.stickerDiv .stickerDivH1, .stickerDiv .stickerDivUser, .stickerDiv .stickerDivImg, .stickerDiv .stickerDivDate, .stickerDiv .stickerDivNearby, .stickerDiv .stickerDivButton {
+ opacity: 0;
+ transition: opacity 0.8s;
+ transition-timing-function: 'ease-in';
+}
+
+.stickerDiv.revealed .stickerDivH1, .stickerDiv.revealed .stickerDivUser, .stickerDiv.revealed .stickerDivImg, .stickerDiv.revealed .stickerDivDate, .stickerDiv.revealed .stickerDivNearby, .stickerDiv.revealed .stickerDivButton {
+ opacity: 1;
+}
+
diff --git a/static/popUp.css b/static/css/popUp.css
similarity index 93%
rename from static/popUp.css
rename to static/css/popUp.css
index e5771e2..7341701 100644
--- a/static/popUp.css
+++ b/static/css/popUp.css
@@ -1,68 +1,68 @@
-.popUpContainer{
- position: fixed;
- top: 0px;
- width: 100%;
- height: 100%;
- background-color: rgba(0,0,0,0.8);
- justify-content: center;
- align-items: center;
- display: none;
- opacity: 0;
- transition: opacity 0.5s;
-}
-
-.showPopUp{
- opacity: 1;
-}
-
-.popUpBox{
- width: 90%;
- max-width: 400px;
- background-color: white;
- display: flex;
- flex-direction: column;
- align-items: center;
- overflow: hidden;
-
- border-radius: 15px;
- box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.8);
-}
-
-.popUpTitle{
- font-size: 30px;
- font-family: Arial;
- padding: 20px;
-}
-
-.popUpContent{
- width: 100%;
- display: flex;
- justify-content: center;
-}
-
-.popUpDescription{
- padding-left: 30px;
- padding-right: 30px;
- font-size: 20px;
- font-family: Arial;
- text-align: center;
-}
-
-.popUpButtons{
- display: flex;
- flex-direction: row;
- width: 100%;
- min-height: 30px;
-}
-
-.popUpButton{
- flex-grow: 1;
- padding: 10px;
- text-align: center;
- margin: 20px;
- border-radius: 10px;
- font-family: Arial;
- font-weight: bold;
- cursor: pointer;
-}
-
+.popUpContainer{
+ position: fixed;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.8);
+ justify-content: center;
+ align-items: center;
+ display: none;
+ opacity: 0;
+ transition: opacity 0.5s;
+}
+
+.showPopUp{
+ opacity: 1;
+}
+
+.popUpBox{
+ width: 90%;
+ max-width: 400px;
+ background-color: white;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow: hidden;
+
+ border-radius: 15px;
+ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.8);
+}
+
+.popUpTitle{
+ font-size: 30px;
+ font-family: Arial;
+ padding: 20px;
+}
+
+.popUpContent{
+ width: 100%;
+ display: flex;
+ justify-content: center;
+}
+
+.popUpDescription{
+ padding-left: 30px;
+ padding-right: 30px;
+ font-size: 20px;
+ font-family: Arial;
+ text-align: center;
+}
+
+.popUpButtons{
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ min-height: 30px;
+}
+
+.popUpButton{
+ flex-grow: 1;
+ padding: 10px;
+ text-align: center;
+ margin: 20px;
+ border-radius: 10px;
+ font-family: Arial;
+ font-weight: bold;
+ cursor: pointer;
+}
+
diff --git a/static/favicon.ico b/static/favicon.ico
deleted file mode 100644
index 8efe884..0000000
Binary files a/static/favicon.ico and /dev/null differ
diff --git a/static/edit.svg b/static/img/edit.svg
similarity index 100%
rename from static/edit.svg
rename to static/img/edit.svg
diff --git a/static/img/favicon.ico b/static/img/favicon.ico
new file mode 100644
index 0000000..0a29f61
Binary files /dev/null and b/static/img/favicon.ico differ
diff --git a/static/img/favicon.svg b/static/img/favicon.svg
new file mode 100644
index 0000000..03b20cd
--- /dev/null
+++ b/static/img/favicon.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/img/line.svg b/static/img/line.svg
new file mode 100644
index 0000000..750aa39
--- /dev/null
+++ b/static/img/line.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/logo.svg b/static/img/logo.svg
similarity index 99%
rename from static/logo.svg
rename to static/img/logo.svg
index b94bb59..01b5a58 100644
--- a/static/logo.svg
+++ b/static/img/logo.svg
@@ -1,37 +1,37 @@
-
-
-
+
+
+
diff --git a/static/logo_compact_outline_wit.png b/static/img/logo_compact_outline_wit.png
similarity index 100%
rename from static/logo_compact_outline_wit.png
rename to static/img/logo_compact_outline_wit.png
diff --git a/static/img/marker.png b/static/img/marker.png
new file mode 100644
index 0000000..72bd601
Binary files /dev/null and b/static/img/marker.png differ
diff --git a/static/img/markerShadow.png b/static/img/markerShadow.png
new file mode 100644
index 0000000..7ce117e
Binary files /dev/null and b/static/img/markerShadow.png differ
diff --git a/static/remove.svg b/static/img/remove.svg
similarity index 100%
rename from static/remove.svg
rename to static/img/remove.svg
diff --git a/static/thumb-down.svg b/static/img/thumb-down.svg
similarity index 100%
rename from static/thumb-down.svg
rename to static/img/thumb-down.svg
diff --git a/static/thumb-up.svg b/static/img/thumb-up.svg
similarity index 100%
rename from static/thumb-up.svg
rename to static/img/thumb-up.svg
diff --git a/static/js/UI.js b/static/js/UI.js
new file mode 100644
index 0000000..b80fc38
--- /dev/null
+++ b/static/js/UI.js
@@ -0,0 +1,55 @@
+//Add view
+var addIsOpen = false;
+addIcon.addEventListener('click', function(){
+ if(!addIsOpen){
+ //Open add view
+ openAddView();
+ addIsOpen = true;
+ //Change icon
+ addIcon.classList.add('addIconClose');
+ } else {
+ //Close add view
+ closeAddView();
+ closeSuccessView();
+ addIsOpen = false;
+ addIcon.classList.remove('addIconClose');
+
+ }
+});
+//Open view
+function openAddView(){
+ resetView();
+ addView.classList.add('openView');
+ setTimeout(function(){
+ getLocation();
+ }, 500);
+}
+
+function closeAddView(){
+ addView.classList.remove('openView');
+}
+
+function openSuccessView(){
+ successView.classList.add('openView');
+}
+
+function closeSuccessView(){
+ successView.classList.remove('openView');
+ addIsOpen = false;
+ addIcon.classList.remove('addIconClose');
+}
+
+//Reset add view
+function resetView(){
+ imageText.classList.remove('addImageTextHidden');
+ imagePreview.classList.remove('addImagePreviewShow');
+ dropArea.classList.remove('invalid');
+ latitudeInput.value = "";
+ longitudeInput.value = "";
+ latitudeInput.disabled = false;
+ longitudeInput.disabled = false;
+ setLocationContainer("", false);
+ imageFile = null;
+ submitButton.classList.remove('addSubmitButtonPressed');
+ setManualLocationInput(false);
+}
\ No newline at end of file
diff --git a/static/admin.js b/static/js/admin.js
similarity index 96%
rename from static/admin.js
rename to static/js/admin.js
index 8e2ed7c..11cc837 100644
--- a/static/admin.js
+++ b/static/js/admin.js
@@ -1,401 +1,401 @@
-const cardHolder = document.getElementsByClassName('cardHolder')[0];
-const logoContainer = document.getElementsByClassName('logoContainer')[0];
-const logoEditBackdrop = document.getElementsByClassName('logoEditBackdrop')[0];
-const logoEditName = document.getElementById('logoEditName');
-const logoEditColor = document.getElementById('logoEditColor');
-const logoEditButton = document.getElementsByClassName('logoEditButton')[0];
-
-
-var maxRotation = 30;
-var maxRotationIn = 200;
-var pivotOn = 100;
-var maxCardsPerLoad = 5;
-
-
-loadCards();
-loadLogos();
-
-function loadCards(){
- //Check if there are 0 cards in the holder
- if(cardHolder.children.length == 0){
- //Create request to server
- var cardRequest = new XMLHttpRequest();
- var url = "getUnverifiedStickers?";
- cardRequest.open('GET', url);
- cardRequest.onreadystatechange = function(){
- if(this.readyState == 4){
- if(this.status == 200){
- var results = JSON.parse(this.responseText);
- var maxX = results.length;
- if(maxX > 5){
- maxX = 5;
- }
- for(var x = 0; x < maxX;x++){
- createCard(results[x][0], results[x][4]);
- }
- } else {
- //HANDLE ERRORS
- alert(this.responseText);
- }
- }
- }
- //Send the request
- cardRequest.send();
- }
-}
-
-function createCard(id, imagesrc){
- //Create card
- var card = document.createElement('div');
- card.classList.add('card');
-
- //Create sticker image
- var image = document.createElement('img');
- image.classList.add('stickerImage');
- image.alt = 'Image of sticker';
- image.src = imagesrc;
-
- //Create the button area
- var buttonArea = document.createElement('div');
- buttonArea.classList.add('buttonArea');
-
- //Create reject button
- var rejectButton = document.createElement('div');
- rejectButton.classList.add("cardButton");
- rejectButton.classList.add("reject");
- rejectButton.addEventListener('click', function(){
- //Animate card
- card.style.animation = "cardSwipeLeft 0.3s";
- setTimeout(function (){
- //Start reject function
- rejectCard();
- }, 300);
- });
-
- //Create accept button
- var acceptButton = document.createElement('div');
- acceptButton.classList.add("cardButton");
- acceptButton.classList.add("accept");
- acceptButton.addEventListener('click', function(){
- //Animate card
- card.style.animation = "cardSwipeRight 0.3s";
- setTimeout(function (){
- //Start accept function
- acceptCard();
- }, 300);
- });
-
-
- card.appendChild(image);
- card.appendChild(buttonArea);
- buttonArea.appendChild(rejectButton);
- buttonArea.appendChild(acceptButton);
-
-
- //Touch actions
- card.addEventListener('touchstart', onTouchStart);
- card.addEventListener('touchmove', onTouchMove);
- card.addEventListener('touchend', onTouchEnd);
- card.addEventListener('mousedown', onTouchStart);
- card.addEventListener('mousemove', onTouchMove);
- card.addEventListener('mouseup', onTouchEnd);
-
- var startX, objectX = 0, dragging;
-
- function onTouchStart(e){
- e.preventDefault();
- card.style.transition = "";
- //Save start cursor position
- if(e.pageX == null){
- startX = e.touches[0].pageX;
- } else {
- startX = e.pageX;
- }
- dragging = true;
- }
-
- function onTouchMove(e){
- e.preventDefault();
- if(dragging == true){
- //Calculate difference X
- if(e.pageX == null){
- deltaX = (startX - e.touches[0].pageX) * -1;
- } else {
- deltaX = (startX - e.pageX) * -1;
- }
- moveCard(deltaX);
- }
- }
-
- function onTouchEnd(e){
- dragging = false;
- if(e.pageX == null){
- objectX -= (startX - e.changedTouches[0].pageX);
- } else {
- objectX -= (startX - e.pageX);
- }
-
- moveCardOnDrop();
- }
-
- //Function to animate the card movement
- function moveCard(deltaX){
- if(objectX + deltaX > (-1 * maxRotationIn) && objectX + deltaX < maxRotationIn){
- var rotation = maxRotation / maxRotationIn * (objectX + deltaX);
- card.style.transform = "translate(" + (objectX + deltaX) + "px, 0) rotate(" + rotation + "deg)";
- var opacity = 1 / 200 * (objectX + deltaX);
- if(opacity < 0){
- opacity *= -1;
- }
- card.style.opacity = 1 - opacity;
- }
- }
-
- function moveCardOnDrop(){
- if(!dragging){
- var acceptSate = 0;
- if(objectX > pivotOn){
- acceptSate = 1;
- } else if (objectX < (pivotOn * -1)){
- acceptSate = -1;
- }
- if(acceptSate == 0){
- //Move card back to center
- objectX = 0;
- card.style.transition = "transform 0.5s, opacity 0.5s";
- card.style.transform = "translate(0px,0px) rotate(0deg)";
- card.style.opacity = 1;
- setTimeout(function(){
- card.style.transition = '';
- }, 500);
- } else if (acceptSate == 1){
- //Move card to right and accept
- objectX = 0;
- card.style.transition = "transform 0.5s, opacity 0.5s";
- card.style.transform = "translate(200px,0px) rotate(30deg)";
- card.style.opacity = 0;
- setTimeout(function(){
- card.style.transition = '';
- //Accept
- acceptCard();
- }, 500);
- } else if (acceptSate == -1){
- //Move card to right and accept
- objectX = 0;
- card.style.transition = "transform 0.5s, opacity 0.5s";
- card.style.transform = "translate(-200px,0px) rotate(-30deg)";
- card.style.opacity = 0;
- setTimeout(function(){
- card.style.transition = '';
- //Decline
- rejectCard();
- }, 500);
- }
- }
- }
-
- function acceptCard(){
- //update sticker
- var updateRequest = new XMLHttpRequest()
- updateRequest.open('GET', '/setSticker?id=' + id + '&state=Verify');
- updateRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status == 400){
- alert(this.responseText);
- }
- };
- updateRequest.send();
- if(cardHolder.children.length == 0){
- loadCards();
- }
- card.remove();
- }
-
- function rejectCard(){
- //update sticker
- var updateRequest = new XMLHttpRequest()
- updateRequest.open('GET', '/setSticker?id=' + id + '&state=Reject');
- updateRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status == 400){
- alert(this.responseText);
- }
- };
- updateRequest.send();
- //Check if new stickers have to be loaded
- if(cardHolder.children.length == 0){
- loadCards();
- }
- card.remove();
- }
- cardHolder.appendChild(card);
-}
-
-function loadLogos(){
- //Clear container
- logoContainer.innerHTML = "";
-
- //Retreive main logo
- const logoRequest = new XMLHttpRequest();
- const logosRequest = new XMLHttpRequest();
- var logo;
- //Get token
- token = getCookieData('token');
- logoRequest.open('GET', '/static/logo.svg');
- logoRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status == 200){
- logo = this.responseText;
- logosRequest.send();
- } else if (this.readyState == 4 && this.status == 404){
- alert("Error: main logo svg not found...");
- }
- }
- logoRequest.send()
-
- //Retreive logo's from db
- logosRequest.open('GET', '/logos?token='+token)
- logosRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status == 200){
- //Parse result to json
- var logosJson = JSON.parse(this.responseText);
- //Add logos to layout
- for(var x = 0; x < logosJson.length; x++){
- let id = logosJson[x][0];
- let name = logosJson[x][1];
- let color = logosJson[x][2];
- //Create row
- var logoRow = document.createElement('div');
- logoRow.classList.add('logoRow');
- //Create logo icon
- var logoIcon = document.createElement('div');
- logoIcon.classList.add('logoIcon');
- logoIcon.innerHTML = logo.replaceAll('COLORPLACE', color);
- //Create logo label
- var logoLabel = document.createElement('span');
- logoLabel.classList.add('logoLabel');
- logoLabel.innerHTML = name;
- //Create logo controls
- var logoControls = document.createElement('div');
- logoControls.classList.add('logoControls');
- //Create remove button
- var logoRemove = document.createElement('img');
- logoRemove.classList.add('logoControl');
- logoRemove.src = '/static/remove.svg';
- logoRemove.addEventListener('click', function(){
- var removePopUp = new PopUp();
- removePopUp.setTitle("Are you sure?");
- removePopUp.setDescription("Are you sure you want to delete this logo?");
- removePopUp.addButton("Yes", "rgb(0, 140, 7)", "white", function(){
- //Remove logo
- var deleteLogoRequest = new XMLHttpRequest();
- deleteLogoRequest.open('DELETE', '/deleteLogo?id='+ id);
- deleteLogoRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status != 200){
- alert(this.responseText);
- }
- }
- deleteLogoRequest.send();
- removePopUp.close();
- loadLogos();
- setTimeout(function(){
- removePopUp.remove();
- }, 500);
- })
- removePopUp.addButton("No", "rgb(240, 0, 36)", "white", function(){
- removePopUp.close();
- setTimeout(function(){
- removePopUp.remove();
- }, 500);
- })
- removePopUp.show();
- });
- //Create edit button
- var logoEdit = document.createElement('img');
- logoEdit.classList.add('logoControl');
- logoEdit.src = '/static/edit.svg';
- logoEdit.addEventListener('click', function(){
- //Edit dialog
- editLogo(id, name, color);
- });
- //Add everything together
- logoRow.appendChild(logoIcon);
- logoRow.appendChild(logoLabel);
- logoControls.appendChild(logoEdit);
- logoControls.appendChild(logoRemove);
- logoRow.appendChild(logoControls);
- logoContainer.appendChild(logoRow);
- }
- //Add 'add logo' buttton
- var logoAddButton = document.createElement('span');
- logoAddButton.classList.add('logoAddButton');
- logoAddButton.innerHTML = "Add logo";
- logoAddButton.addEventListener('click', function(){
- editLogo();
- });
- logoContainer.appendChild(logoAddButton);
- } else if (this.readyState == 4 && this.status == 404){
- alert("Could not connect to API...");
- }
- }
-}
-
-function editLogo(id = null, name = "", color = ""){
- if(id == null){
- document.getElementsByClassName('logoEditTitle')[0].innerHTML = "Add logo";
- }
-
- logoEditBackdrop.style.display = "flex";
- logoEditName.value = name;
- logoEditColor.value = color;
- setTimeout(function(){
- logoEditBackdrop.style.opacity = "1";
- }, 10);
-
- logoEditButton.onclick = function(){
- //Check if the user wanted to create a new logo or edit one
- if(id == null){
- //Create new logo
- var logoAddRequest = new XMLHttpRequest();
- logoAddRequest.open('POST', '/addLogo?&name=' + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value));
- logoAddRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status == 200){
- //Logo added!
- logoEditBackdrop.style.opacity = 0;
- setTimeout(function(){
- logoEditBackdrop.style.display = 'none';
- }, 500);
- loadLogos();
- } else if (this.readyState == 4){
- alert(this.responseText);
- }
- }
- logoAddRequest.send();
- } else {
- //Update logo
- var logoUpdateRequest = new XMLHttpRequest();
- logoUpdateRequest.open('PATCH', '/editLogo?id=' + id + "&name=" + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value));
- logoUpdateRequest.onreadystatechange = function(){
- if(this.readyState == 4 && this.status == 200){
- //Logo added!
- logoEditBackdrop.style.opacity = 0;
- setTimeout(function(){
- logoEditBackdrop.style.display = 'none';
- }, 500);
- loadLogos();
- } else if (this.readyState == 4){
- alert(this.responseText);
- }
- }
- logoUpdateRequest.send();
- }
- }
-}
-
-function getCookieData( name ) {
- var pairs = document.cookie.split("; "),
- count = pairs.length, parts;
- while ( count-- ) {
- parts = pairs[count].split("=");
- if ( parts[0] === name )
- return parts[1];
- }
- return false;
+const cardHolder = document.getElementsByClassName('cardHolder')[0];
+const logoContainer = document.getElementsByClassName('logoContainer')[0];
+const logoEditBackdrop = document.getElementsByClassName('logoEditBackdrop')[0];
+const logoEditName = document.getElementById('logoEditName');
+const logoEditColor = document.getElementById('logoEditColor');
+const logoEditButton = document.getElementsByClassName('logoEditButton')[0];
+
+
+var maxRotation = 30;
+var maxRotationIn = 200;
+var pivotOn = 100;
+var maxCardsPerLoad = 5;
+
+
+loadCards();
+loadLogos();
+
+function loadCards(){
+ //Check if there are 0 cards in the holder
+ if(cardHolder.children.length == 0){
+ //Create request to server
+ var cardRequest = new XMLHttpRequest();
+ var url = "getUnverifiedStickers?";
+ cardRequest.open('GET', url);
+ cardRequest.onreadystatechange = function(){
+ if(this.readyState == 4){
+ if(this.status == 200){
+ var results = JSON.parse(this.responseText);
+ var maxX = results.length;
+ if(maxX > 5){
+ maxX = 5;
+ }
+ for(var x = 0; x < maxX;x++){
+ createCard(results[x][0], results[x][4]);
+ }
+ } else {
+ //HANDLE ERRORS
+ alert(this.responseText);
+ }
+ }
+ }
+ //Send the request
+ cardRequest.send();
+ }
+}
+
+function createCard(id, imagesrc){
+ //Create card
+ var card = document.createElement('div');
+ card.classList.add('card');
+
+ //Create sticker image
+ var image = document.createElement('img');
+ image.classList.add('stickerImage');
+ image.alt = 'Image of sticker';
+ image.src = imagesrc;
+
+ //Create the button area
+ var buttonArea = document.createElement('div');
+ buttonArea.classList.add('buttonArea');
+
+ //Create reject button
+ var rejectButton = document.createElement('div');
+ rejectButton.classList.add("cardButton");
+ rejectButton.classList.add("reject");
+ rejectButton.addEventListener('click', function(){
+ //Animate card
+ card.style.animation = "cardSwipeLeft 0.3s";
+ setTimeout(function (){
+ //Start reject function
+ rejectCard();
+ }, 300);
+ });
+
+ //Create accept button
+ var acceptButton = document.createElement('div');
+ acceptButton.classList.add("cardButton");
+ acceptButton.classList.add("accept");
+ acceptButton.addEventListener('click', function(){
+ //Animate card
+ card.style.animation = "cardSwipeRight 0.3s";
+ setTimeout(function (){
+ //Start accept function
+ acceptCard();
+ }, 300);
+ });
+
+
+ card.appendChild(image);
+ card.appendChild(buttonArea);
+ buttonArea.appendChild(rejectButton);
+ buttonArea.appendChild(acceptButton);
+
+
+ //Touch actions
+ card.addEventListener('touchstart', onTouchStart);
+ card.addEventListener('touchmove', onTouchMove);
+ card.addEventListener('touchend', onTouchEnd);
+ card.addEventListener('mousedown', onTouchStart);
+ card.addEventListener('mousemove', onTouchMove);
+ card.addEventListener('mouseup', onTouchEnd);
+
+ var startX, objectX = 0, dragging;
+
+ function onTouchStart(e){
+ e.preventDefault();
+ card.style.transition = "";
+ //Save start cursor position
+ if(e.pageX == null){
+ startX = e.touches[0].pageX;
+ } else {
+ startX = e.pageX;
+ }
+ dragging = true;
+ }
+
+ function onTouchMove(e){
+ e.preventDefault();
+ if(dragging == true){
+ //Calculate difference X
+ if(e.pageX == null){
+ deltaX = (startX - e.touches[0].pageX) * -1;
+ } else {
+ deltaX = (startX - e.pageX) * -1;
+ }
+ moveCard(deltaX);
+ }
+ }
+
+ function onTouchEnd(e){
+ dragging = false;
+ if(e.pageX == null){
+ objectX -= (startX - e.changedTouches[0].pageX);
+ } else {
+ objectX -= (startX - e.pageX);
+ }
+
+ moveCardOnDrop();
+ }
+
+ //Function to animate the card movement
+ function moveCard(deltaX){
+ if(objectX + deltaX > (-1 * maxRotationIn) && objectX + deltaX < maxRotationIn){
+ var rotation = maxRotation / maxRotationIn * (objectX + deltaX);
+ card.style.transform = "translate(" + (objectX + deltaX) + "px, 0) rotate(" + rotation + "deg)";
+ var opacity = 1 / 200 * (objectX + deltaX);
+ if(opacity < 0){
+ opacity *= -1;
+ }
+ card.style.opacity = 1 - opacity;
+ }
+ }
+
+ function moveCardOnDrop(){
+ if(!dragging){
+ var acceptSate = 0;
+ if(objectX > pivotOn){
+ acceptSate = 1;
+ } else if (objectX < (pivotOn * -1)){
+ acceptSate = -1;
+ }
+ if(acceptSate == 0){
+ //Move card back to center
+ objectX = 0;
+ card.style.transition = "transform 0.5s, opacity 0.5s";
+ card.style.transform = "translate(0px,0px) rotate(0deg)";
+ card.style.opacity = 1;
+ setTimeout(function(){
+ card.style.transition = '';
+ }, 500);
+ } else if (acceptSate == 1){
+ //Move card to right and accept
+ objectX = 0;
+ card.style.transition = "transform 0.5s, opacity 0.5s";
+ card.style.transform = "translate(200px,0px) rotate(30deg)";
+ card.style.opacity = 0;
+ setTimeout(function(){
+ card.style.transition = '';
+ //Accept
+ acceptCard();
+ }, 500);
+ } else if (acceptSate == -1){
+ //Move card to right and accept
+ objectX = 0;
+ card.style.transition = "transform 0.5s, opacity 0.5s";
+ card.style.transform = "translate(-200px,0px) rotate(-30deg)";
+ card.style.opacity = 0;
+ setTimeout(function(){
+ card.style.transition = '';
+ //Decline
+ rejectCard();
+ }, 500);
+ }
+ }
+ }
+
+ function acceptCard(){
+ //update sticker
+ var updateRequest = new XMLHttpRequest()
+ updateRequest.open('GET', '/setSticker?id=' + id + '&state=Verify');
+ updateRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 400){
+ alert(this.responseText);
+ }
+ };
+ updateRequest.send();
+ if(cardHolder.children.length == 0){
+ loadCards();
+ }
+ card.remove();
+ }
+
+ function rejectCard(){
+ //update sticker
+ var updateRequest = new XMLHttpRequest()
+ updateRequest.open('GET', '/setSticker?id=' + id + '&state=Reject');
+ updateRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 400){
+ alert(this.responseText);
+ }
+ };
+ updateRequest.send();
+ //Check if new stickers have to be loaded
+ if(cardHolder.children.length == 0){
+ loadCards();
+ }
+ card.remove();
+ }
+ cardHolder.appendChild(card);
+}
+
+function loadLogos(){
+ //Clear container
+ logoContainer.innerHTML = "";
+
+ //Retreive main logo
+ const logoRequest = new XMLHttpRequest();
+ const logosRequest = new XMLHttpRequest();
+ var logo;
+ //Get token
+ token = getCookieData('token');
+ logoRequest.open('GET', '/static/img/logo.svg');
+ logoRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 200){
+ logo = this.responseText;
+ logosRequest.send();
+ } else if (this.readyState == 4 && this.status == 404){
+ alert("Error: main logo svg not found...");
+ }
+ }
+ logoRequest.send()
+
+ //Retreive logo's from db
+ logosRequest.open('GET', '/logos?token='+token)
+ logosRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 200){
+ //Parse result to json
+ var logosJson = JSON.parse(this.responseText);
+ //Add logos to layout
+ for(var x = 0; x < logosJson.length; x++){
+ let id = logosJson[x][0];
+ let name = logosJson[x][1];
+ let color = logosJson[x][2];
+ //Create row
+ var logoRow = document.createElement('div');
+ logoRow.classList.add('logoRow');
+ //Create logo icon
+ var logoIcon = document.createElement('div');
+ logoIcon.classList.add('logoIcon');
+ logoIcon.innerHTML = logo.replaceAll('COLORPLACE', color);
+ //Create logo label
+ var logoLabel = document.createElement('span');
+ logoLabel.classList.add('logoLabel');
+ logoLabel.innerHTML = name;
+ //Create logo controls
+ var logoControls = document.createElement('div');
+ logoControls.classList.add('logoControls');
+ //Create remove button
+ var logoRemove = document.createElement('img');
+ logoRemove.classList.add('logoControl');
+ logoRemove.src = '/static/img/remove.svg';
+ logoRemove.addEventListener('click', function(){
+ var removePopUp = new PopUp();
+ removePopUp.setTitle("Are you sure?");
+ removePopUp.setDescription("Are you sure you want to delete this logo?");
+ removePopUp.addButton("Yes", "rgb(0, 140, 7)", "white", function(){
+ //Remove logo
+ var deleteLogoRequest = new XMLHttpRequest();
+ deleteLogoRequest.open('DELETE', '/deleteLogo?id='+ id);
+ deleteLogoRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status != 200){
+ alert(this.responseText);
+ }
+ }
+ deleteLogoRequest.send();
+ removePopUp.close();
+ loadLogos();
+ setTimeout(function(){
+ removePopUp.remove();
+ }, 500);
+ })
+ removePopUp.addButton("No", "rgb(240, 0, 36)", "white", function(){
+ removePopUp.close();
+ setTimeout(function(){
+ removePopUp.remove();
+ }, 500);
+ })
+ removePopUp.show();
+ });
+ //Create edit button
+ var logoEdit = document.createElement('img');
+ logoEdit.classList.add('logoControl');
+ logoEdit.src = '/static/img/edit.svg';
+ logoEdit.addEventListener('click', function(){
+ //Edit dialog
+ editLogo(id, name, color);
+ });
+ //Add everything together
+ logoRow.appendChild(logoIcon);
+ logoRow.appendChild(logoLabel);
+ logoControls.appendChild(logoEdit);
+ logoControls.appendChild(logoRemove);
+ logoRow.appendChild(logoControls);
+ logoContainer.appendChild(logoRow);
+ }
+ //Add 'add logo' buttton
+ var logoAddButton = document.createElement('span');
+ logoAddButton.classList.add('logoAddButton');
+ logoAddButton.innerHTML = "Add logo";
+ logoAddButton.addEventListener('click', function(){
+ editLogo();
+ });
+ logoContainer.appendChild(logoAddButton);
+ } else if (this.readyState == 4 && this.status == 404){
+ alert("Could not connect to API...");
+ }
+ }
+}
+
+function editLogo(id = null, name = "", color = ""){
+ if(id == null){
+ document.getElementsByClassName('logoEditTitle')[0].innerHTML = "Add logo";
+ }
+
+ logoEditBackdrop.style.display = "flex";
+ logoEditName.value = name;
+ logoEditColor.value = color;
+ setTimeout(function(){
+ logoEditBackdrop.style.opacity = "1";
+ }, 10);
+
+ logoEditButton.onclick = function(){
+ //Check if the user wanted to create a new logo or edit one
+ if(id == null){
+ //Create new logo
+ var logoAddRequest = new XMLHttpRequest();
+ logoAddRequest.open('POST', '/addLogo?&name=' + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value));
+ logoAddRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 200){
+ //Logo added!
+ logoEditBackdrop.style.opacity = 0;
+ setTimeout(function(){
+ logoEditBackdrop.style.display = 'none';
+ }, 500);
+ loadLogos();
+ } else if (this.readyState == 4){
+ alert(this.responseText);
+ }
+ }
+ logoAddRequest.send();
+ } else {
+ //Update logo
+ var logoUpdateRequest = new XMLHttpRequest();
+ logoUpdateRequest.open('PATCH', '/editLogo?id=' + id + "&name=" + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value));
+ logoUpdateRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 200){
+ //Logo added!
+ logoEditBackdrop.style.opacity = 0;
+ setTimeout(function(){
+ logoEditBackdrop.style.display = 'none';
+ }, 500);
+ loadLogos();
+ } else if (this.readyState == 4){
+ alert(this.responseText);
+ }
+ }
+ logoUpdateRequest.send();
+ }
+ }
+}
+
+function getCookieData( name ) {
+ var pairs = document.cookie.split("; "),
+ count = pairs.length, parts;
+ while ( count-- ) {
+ parts = pairs[count].split("=");
+ if ( parts[0] === name )
+ return parts[1];
+ }
+ return false;
}
\ No newline at end of file
diff --git a/static/js/common.js b/static/js/common.js
new file mode 100644
index 0000000..f06a5aa
--- /dev/null
+++ b/static/js/common.js
@@ -0,0 +1,26 @@
+const addView = document.getElementsByClassName('addView')[0];
+const successView = document.getElementsByClassName('successView')[0];
+const imageText = document.getElementsByClassName('addImageText')[0];
+const imagePreview = document.getElementsByClassName('addImagePreview')[0];
+const dropArea = document.getElementsByClassName('addImageInput')[0];
+const latitudeInput = document.getElementById('latitudeInput');
+const longitudeInput = document.getElementById('longitudeInput');
+const locationContainerText = document.getElementsByClassName('locationContainerText')[0];
+const locationIcon = document.getElementsByClassName('locationIcon')[0];
+const locationContainerSpinner = document.getElementsByClassName('locationContainerSpinner')[0];
+const submitButton = document.getElementsByClassName('addSubmitButton')[0];
+const addLogoSelector = document.getElementsByClassName('addLogoSelector')[0];
+const addIcon = document.getElementsByClassName('addIcon')[0];
+const emailInput = document.getElementsByClassName('successEmail')[0];
+
+const nearYouButton = document.getElementsByClassName('nearYouButton')[0];
+
+var imageFile;
+var selectedLogo;
+var selectedLogoId;
+var emailCode;
+
+var pointersOnMap = [];
+var logoIcons = [];
+
+dayjs.extend(window.dayjs_plugin_relativeTime)
\ No newline at end of file
diff --git a/static/js/location.js b/static/js/location.js
new file mode 100644
index 0000000..51f6fad
--- /dev/null
+++ b/static/js/location.js
@@ -0,0 +1,79 @@
+// *****LOCATION
+function getLocation(){
+ //Get the permissions
+ setLocationContainer("Please grant location permission...");
+
+ if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(handleLocation, showError);
+ } else {
+ alert("Geolocation is not supported by this browser.");
+ }
+}
+
+function handleLocation(position){
+ //Set the values in the inputs
+ setLocationContainer("Loading location...");
+ latitudeInput.value = position.coords.latitude;
+ longitudeInput.value = position.coords.longitude;
+ //Retrieve estimated address
+ var addressRequest = new XMLHttpRequest();
+ addressRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 200){
+ var addressJson = JSON.parse(addressRequest.responseText);
+ var text = "Location: nearby ";
+ if(addressJson['address']['road'] != undefined){
+ text += addressJson['address']['road'];
+ if(addressJson['address']['house_number'] != undefined){
+ text += ' ' + addressJson['address']['house_number'];
+ }
+ }
+ text += "
Click to enter manually."
+ setLocationContainer(text, true);
+ }
+ }
+ console.log('https://nominatim.openstreetmap.org/reverse?lat=' + position.coords.latitude + '&lon=' + position.coords.longitude + '&format=json');
+ addressRequest.open("GET", 'https://nominatim.openstreetmap.org/reverse?lat=' + position.coords.latitude + '&lon=' + position.coords.longitude + '&format=json', true);
+ addressRequest.send();
+}
+
+function showError(error) {
+ switch(error.code) {
+ case error.PERMISSION_DENIED:
+ setLocationContainer("Location permission denied, please enter manually.");
+ break;
+ case error.POSITION_UNAVAILABLE:
+ setLocationContainer("Location information is unavailable, please enter manually.");
+ break;
+ case error.TIMEOUT:
+ setLocationContainer("The request to get user location timed out, please enter manually.");
+ break;
+ case error.UNKNOWN_ERROR:
+ setLocationContainer("An unknown error occurred, please enter manually");
+ break;
+ }
+}
+
+function setLocationContainer(text, found=false){
+ locationContainerText.innerHTML = text;
+ if(found == true){
+ locationContainerSpinner.style.display = "none";
+ locationIcon.style.display = "block";
+ } else {
+ locationContainerSpinner.style.display = "block";
+ locationIcon.style.display = "none";
+ }
+}
+
+function setManualLocationInput(state){
+ if(state == true){
+ latitudeInput.style.display = 'block';
+ longitudeInput.style.display = 'block';
+ document.getElementsByClassName('locationInputDes')[0].style.display = 'block';
+ document.getElementsByClassName('locationInputDes')[1].style.display = 'block';
+ } else {
+ latitudeInput.style.display = 'none';
+ longitudeInput.style.display = 'none';
+ document.getElementsByClassName('locationInputDes')[0].style.display = 'none';
+ document.getElementsByClassName('locationInputDes')[1].style.display = 'none';
+ }
+}
\ No newline at end of file
diff --git a/static/js/main.js b/static/js/main.js
new file mode 100644
index 0000000..445b04c
--- /dev/null
+++ b/static/js/main.js
@@ -0,0 +1,108 @@
+// ****LOGO ADDER
+//Load the logo for the add view and the icons
+var logoSourceRequest = new XMLHttpRequest();
+logoSourceRequest.open('GET', 'static/img/logo.svg', true);
+logoSourceRequest.onreadystatechange = function(){
+ if(this.readyState == 4 && this.status == 200){
+ //Get the logo
+ const logo = this.responseText;
+ // Make request to server to get all logos
+ var logoRequest = new XMLHttpRequest();
+ logoRequest.open('GET', 'logos', true);
+
+ logoRequest.onreadystatechange = function() {
+ if(this.readyState == 4 && this.status == 200){
+ //Create JSON object of response
+ var logoJson = JSON.parse(this.responseText);
+ for(var i = 0; i < logoJson.length; i ++){
+ const color = logoJson[i][2];
+ const logoId = logoJson[i][0];
+ const title = logoJson[i][1];
+ //Create the object
+ const logoElement = document.createElement('div');
+ logoElement.classList.add('logoElement');
+ logoElement.innerHTML = logo.replaceAll('COLORPLACE', color);
+ logoElement.style.width = '50px';
+
+ //Handle selection
+ logoElement.addEventListener('click', function(){
+ if(selectedLogo != null){
+ selectedLogo.classList.remove('logoElementSelected');
+ }
+ logoElement.classList.add('logoElementSelected');
+ selectedLogo = logoElement;
+ selectedLogoId = logoId;
+ });
+
+ //Select if first one
+ if(i == 0){
+ logoElement.classList.add('logoElementSelected');
+ selectedLogo = logoElement;
+ selectedLogoId = logoId;
+ }
+
+ addLogoSelector.appendChild(logoElement);
+
+ //Create icon for map
+ const logoIcon = {
+ id: logoId,
+ icon: L.divIcon({
+ className: 'customDivIcon',
+ html: logo.replaceAll('COLORPLACE', color),
+ iconSize: [40, 40],
+ iconAnchor: [24, 24]
+ })
+ }
+ logoIcons.push(logoIcon);
+ }
+ //Logo's ready, map ready
+ updateMap();
+ }
+ }
+ logoRequest.send();
+ }
+}
+logoSourceRequest.send();
+
+
+//Update email
+function updateEmail(){
+ if(emailInput.value != ''){
+ //Check if email is valid
+ if(checkEmail(emailInput.value)){
+ //Update email
+ emailInput.classList.remove('invalid');
+ var request = new XMLHttpRequest();
+ request.open('PATCH', 'addEmail', true);
+ var formData = new FormData();
+ formData.append('email', emailInput.value);
+ formData.append('token', emailCode);
+ request.onreadystatechange = function(){
+ if(this.readyState == 4){
+ if(this.status == 200){
+ closeSuccessView();
+ } else {
+ alert('Email could not be added :(');
+ closeSuccessView();
+ }
+ }
+ }
+ request.send(formData);
+ } else {
+ //Email not valid, update UI
+ emailInput.classList.add('invalid');
+ }
+ } else {
+ //No email added close success view
+ closeSuccessView()
+ }
+}
+
+function checkEmail(email){
+ if(email.includes('@') && email.includes('.')){
+ return true;
+ }
+ else{
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/static/js/map.js b/static/js/map.js
new file mode 100644
index 0000000..c16921d
--- /dev/null
+++ b/static/js/map.js
@@ -0,0 +1,121 @@
+// ***** MAP
+// Create a map
+var mymap = L.map('map').setView([52.087299, 5.165430], 13);
+
+// Give the map a source
+L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ maxZoom: 19,
+ attribution: '© OpenStreetMap contributors'
+}).addTo(mymap);
+
+// Update the pointers
+mymap.on('moveend', updateMap);
+
+// Add a button for the current location
+L.control.locate().addTo(mymap);
+
+// Update the map so it always renders the markers on page load
+updateMap();
+
+function updateMap() {
+ //Remove pointers that fell of the map
+ pointersOnMap = pointersOnMap.filter(function(pointer){
+ if(pointer.lat < mymap.getBounds().getSouth() || pointer.lat > mymap.getBounds().getNorth() || pointer.lon < mymap.getBounds().getWest() || pointer.lon > mymap.getBounds().getEast()){
+ mymap.removeLayer(pointer.pointer);
+ return false;
+ }
+ return true;
+ });
+ //Add new pointers on the map
+ var request = new XMLHttpRequest();
+ var url = 'getStickers?north=' + mymap.getBounds().getNorth();
+ url += '&south=' + mymap.getBounds().getSouth();
+ url += '&west=' + mymap.getBounds().getWest();
+ url += '&east=' + mymap.getBounds().getEast();
+ request.open('GET', url, true);
+ request.onreadystatechange = function(){
+ if(this.readyState == 4){
+ if(this.status == 200){
+ var results = JSON.parse(this.responseText);
+ for(var x = 0; x < results.length; x++){
+ //Check if the pointer already exists on map
+ var isNotOnMap = true;
+ for(var y = 0; y < pointersOnMap.length; y++){
+ if(results[x][0] == pointersOnMap[y].id){
+ isNotOnMap = false;
+ }
+ }
+ if(isNotOnMap){
+ //Not on map, add the pointer
+ var stickyIcon = L.icon({
+ iconUrl: '/static/img/marker.png',
+ shadowUrl: '/static/img/markerShadow.png',
+ iconSize: [38, 52],
+ shadowSize: [52, 52],
+ shadowAnchor: [19, 25],
+ });
+
+ const pointer = {
+ id: results[x][0],
+ lat: results[x][1],
+ lon: results[x][2],
+ pointer: L.marker([results[x][1], results[x][2]], {icon: stickyIcon}).addTo(mymap)
+ }
+
+ //Add a popup
+ let spotText = "";
+ if (results[x][7] === 1) {
+ spotText = "spot";
+ }
+ else {
+ spotText = "spots";
+ }
+
+ pointer.pointer.bindPopup(`
+