diff --git a/.DS_Store b/.DS_Store
index ecbd8745b852288b7ee3372bccde4f5e9307ec7d..30f3834918176a2c2f81213b84951d66311c943f 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/Lab_3/.DS_Store b/Lab_3/.DS_Store
index 83a1aa3fd55c0ed8fb03b5810757026de4044fb2..38e64d943bd45b70ad540b986a392aaf8d1e02b2 100644
Binary files a/Lab_3/.DS_Store and b/Lab_3/.DS_Store differ
diff --git a/Lab_4/.DS_Store b/Lab_4/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..cd477194c8e26360739930522ab27ccf72594546
Binary files /dev/null and b/Lab_4/.DS_Store differ
diff --git a/Lab_4/Lab4_TDDD97.pdf b/Lab_4/Lab4_TDDD97.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..c7a742939df36c247406b95ce3dedcc23ad3982e
Binary files /dev/null and b/Lab_4/Lab4_TDDD97.pdf differ
diff --git a/Lab_4/Twidder/.DS_Store b/Lab_4/Twidder/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..aeb683ff2abafaefdd695afac5298539b9a4f169
Binary files /dev/null and b/Lab_4/Twidder/.DS_Store differ
diff --git a/Lab_4/Twidder/__pycache__/database_helper.cpython-38.pyc b/Lab_4/Twidder/__pycache__/database_helper.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7e4a18eb4153651973e42af292cfa4aab521d882
Binary files /dev/null and b/Lab_4/Twidder/__pycache__/database_helper.cpython-38.pyc differ
diff --git a/Lab_4/Twidder/database b/Lab_4/Twidder/database
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Lab_4/Twidder/database.db b/Lab_4/Twidder/database.db
new file mode 100644
index 0000000000000000000000000000000000000000..b8863ccbb4f1c57137f8f44aa97d3cac6c1560e4
Binary files /dev/null and b/Lab_4/Twidder/database.db differ
diff --git a/Lab_4/Twidder/database_helper.py b/Lab_4/Twidder/database_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..31c99f0531e7f57e98969af95424b4399729d5ea
--- /dev/null
+++ b/Lab_4/Twidder/database_helper.py
@@ -0,0 +1,73 @@
+"""Database Helper"""
+import sqlite3
+from sqlite3 import *
+from flask import g
+
+DATABASE_URL = 'database.db'
+
+def get_db():
+    """Connect to database"""
+    db = getattr(g, 'db', None)
+    if db is None:
+        db = g.db = sqlite3.connect(DATABASE_URL)
+    return db
+    
+def close_db():
+    """Disconnect"""
+    try:
+        db = getattr(g, 'db', None)
+        if db is not None:
+            db.close()
+            return True
+        else:
+            return False
+    except:
+        return False
+
+def create_user(email, password, firstname, familyname, gender, city, country):
+    """Create user"""
+    try:
+        get_db().execute("INSERT into user (email, password, firstname, familyname, gender, city, country) VALUES (?,?,?,?,?,?,?)",(email, password, firstname, familyname, gender, city, country))
+        print("Success, User has been inserted into database")
+        get_db().commit()
+        return True
+    except:
+        return False
+
+def update_user(new_password, email):
+    """Update user"""
+    try:
+        get_db().execute("UPDATE user SET password = ? WHERE email = ?", (new_password,email))
+        get_db().commit()
+        return True
+    except:
+        return False
+
+def create_post(my_email, destination_email, message):
+    """Add a messages to the database"""
+    try:
+        get_db().execute("INSERT INTO messages (email, emailPostedBy, message) VALUES (?, ?, ?)",(destination_email, my_email, message))
+        get_db().commit()
+        return True
+    except:
+        return False
+
+def get_post(email):
+    """Get all messages from selected user from the database"""
+    try:  
+        cursor = get_db().execute("SELECT * FROM messages WHERE email = ? ",[email])
+        posts = cursor.fetchall()
+        cursor.close()
+        return posts
+    except Exception as e:
+        return []
+
+def find_user(email):
+    """Read user by email"""
+    try:
+        cursor = get_db().execute("SELECT * FROM user WHERE email like ?", [email])
+        rows = cursor.fetchone()
+        cursor.close()
+        return rows
+    except:
+        return []
diff --git a/Lab_4/Twidder/schema.sql b/Lab_4/Twidder/schema.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9ef51ab6c057777700d879dbd0e758e18187e194
--- /dev/null
+++ b/Lab_4/Twidder/schema.sql
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS user;
+DROP TABLE IF EXISTS messages;
+
+CREATE TABLE IF NOT EXISTS user(email VARCHAR(50) NOT NULL PRIMARY KEY,
+                                password VARCHAR(50) NOT NULL,
+                                firstname VARCHAR(50),
+                                familyname VARCHAR(50),
+                                gender VARCHAR(50),
+                                city VARCHAR(50),
+                                country VARCHAR(50)
+                                );
+
+CREATE TABLE IF NOT EXISTS messages(email VARCHAR(50),
+                                    emailPostedBy VARCHAR(50),
+                                    message VARCHAR(50)
+                                    );
+
+INSERT INTO user (email, password, firstname, familyname, gender, city, country)
+    VALUES ("o@o", "oo", "o", "o", "male", "o", "o");
+
+INSERT INTO user (email, password, firstname, familyname, gender, city, country)
+    VALUES ("a@a", "aa", "a", "a", "male", "a", "a");
+
+INSERT INTO user (email, password, firstname, familyname, gender, city, country)
+    VALUES ("b@b", "bb", "b", "b", "male", "b", "b");
diff --git a/Lab_4/Twidder/server.py b/Lab_4/Twidder/server.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8b132cbeecff3417d820e9a1b25d4e9a0ba1be1
--- /dev/null
+++ b/Lab_4/Twidder/server.py
@@ -0,0 +1,379 @@
+"""Server"""
+from flask import Flask, jsonify, request, make_response
+from gevent.pywsgi import WSGIServer
+import uuid
+import database_helper
+import json
+from flask_sock import Sock
+from gevent import monkey
+monkey.patch_all()
+
+
+#Remember:
+#PUT for updating data, POST for adding new data
+#save token on client and server (lab 3)
+# source /Users/psyfax/TDDD97/bin/activate
+# source "/Users/lorenzo/OneDrive - Linköpings universitet/Skola/DI3B/TDDD97/virtualenv/bin/activate"
+# python3 server.py
+# http://127.0.0.1:5000/myServer
+# sqlite3 database.db ".read schema.sql"
+
+#Questions:
+#Why does localhost in URL not work?
+
+
+
+app = Flask(__name__, static_url_path = '/static')#in case flask does not recognize folder
+sock = Sock(app)
+
+app.debug = True
+session = {'token': ("email", "wsObj")}
+
+
+
+@app.route('/')
+def root():
+    return app.send_static_file('client.html')
+
+@app.route('/myServer')
+def myServer():
+    return app.send_static_file('client.html')
+
+def token_has_error(token):
+    """All token standard error checks"""
+    if token is None:
+        # "Server received no token"            
+        return True, 401     
+    if len(token) > 50:
+        # "Server received too long token"     
+        return True, 401
+    if token not in session:
+        #"User not signed in or invalid access token"  
+        return True, 401
+    return False, 0
+
+def input_has_error(input):
+    """All standard input error checks"""
+    try:
+        str = request.get_json()[input]
+    except:
+        return jsonify({}), 400, ""
+    if str is None or str == "":        # "Server received no " + str  
+        return True, 400, ""   
+    if len(str) > 50:                        # "Server received too long " + str     
+        return True, 400, ""
+    return False, 0, str
+
+
+@sock.route('/myServer/api')
+def echo(socket):
+    while True:
+
+        # Making sure we have a valid socket
+        if not socket:
+            return
+
+        # Making sure message format is OK and store email & token in string
+        data = socket.receive()
+        try:
+            myEmail = json.loads(data)["email"]
+        except:
+            return
+        try:
+            myToken = json.loads(data)["token"]
+        except:
+            return
+
+        # sign out if I am logged in somewhere else
+        print(session)
+        for token in list(session.keys()):
+            if session[token][0] == myEmail and token != myToken:
+                if session[token][1] != "":
+                    session[token][1].send(json.dumps({"action" : "signOut"}))
+                    session[token][1].close()
+                print("You got kicked out")
+                session.pop(token)
+        print(session)
+
+        # Put socket in global dict so server knows my connection is open
+        session[myToken] = (myEmail, socket)
+        print(session)
+        socket.send(json.dumps({"action" : "signIn"}))
+    
+
+@app.route("/myServer/sign_in", methods=['POST'])
+def sign_in():
+    """Sign in user"""
+    
+    # Validate Email
+    tmp = input_has_error('email')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    email = tmp[2]
+
+    # Validate Password
+    tmp = input_has_error('password')
+    if tmp[0]:
+        return jsonify({}), tmp[1] 
+    password = tmp[2]    
+
+    # Do the user have an account?
+    rows = database_helper.find_user(email)
+    if rows is None or rows == []:
+        return jsonify({}), 404  #"No user found by your email" 
+
+    if password != rows[1]:
+        return jsonify({}), 401 #"Incorrect password")          
+
+    # Generate a random token 
+    token = str(uuid.uuid4())
+    session[token] = (email, "")
+    
+    # return the token in the Authorization header
+    response = make_response(jsonify({})) #"Server inserted user data into database"   
+    response.headers.add("Access-Control-Allow-Origin", "*")
+    response.headers["Authorization"] = token
+    return response, 204
+
+
+@app.route("/myServer/sign_up", methods=['POST'])
+def sign_up():
+    """Sign up a user"""
+
+    tmp = input_has_error('email')
+    if tmp[0]:
+        print(tmp[1])
+        return jsonify({}), tmp[1]
+    email = tmp[2]
+
+    # Checking that the user does not already exist
+    if database_helper.find_user(email) is not None:
+        return jsonify({}), 409   #"Error: User already exists"     
+    
+    # Går att göra på ett annat sätt med färre function calls
+    # Error checks & storing in local variables
+    tmp = input_has_error('password')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    password = tmp[2]
+    tmp = input_has_error('firstname') 
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    firstname = tmp[2]    
+    tmp = input_has_error('familyname')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    familyname = tmp[2]
+    tmp = input_has_error('gender')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    gender = tmp[2]
+    tmp = input_has_error('city')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    city = tmp[2]
+    tmp = input_has_error('country') 
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    country = tmp[2]
+
+    # Attempts to insert the user data to the database
+    if database_helper.create_user(email, password, firstname, familyname, gender, city, country):
+        return jsonify({}), 204 #"Server inserted user data into database"
+    else:
+        return jsonify({}), 500 #"General Error: Server failed to insert user data into database" 
+
+
+@app.route("/myServer/sign_out", methods=['POST'])
+def sign_out():
+    """Sign out user"""
+    token = request.headers["Authorization"]
+    
+    # Validate Token 
+    tmp =  token_has_error(token)
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    
+    # Close my socket
+    print(session)
+    try:
+        session[token][1].close()
+    except:
+        pass # samma sak som ingenting
+    print(session)
+
+    # set user to not logged in
+    session.pop(token)
+
+    return jsonify({}), 204 # "Successfully signed out")
+
+
+@app.route("/myServer/change_password", methods=['PUT'])
+def change_password():
+    """Change password for the current user"""
+    token = request.headers["Authorization"]
+
+    # Validate Token 
+    tmp = token_has_error(token)
+    if tmp[0]:
+        print("validate token")
+        return jsonify({}), tmp[1]
+
+    # Validate Old Password
+    tmp = input_has_error('old_password')
+    if tmp[0]:
+        print("validate old password")
+        return jsonify({}), tmp[1]
+    old_password = tmp[2]
+
+    # Validate New Password
+    tmp = input_has_error('new_password')
+    if tmp[0]:
+        print("validate new password")
+        return jsonify({}), tmp[1]
+    new_password = tmp[2]
+
+    # Extracting theemail of the current user
+    email = session[token][0]
+
+    # Validation of the old password and attemption to change it to the new one
+    if old_password == database_helper.find_user(email)[1]: #checks if old_password is correct
+        status = database_helper.update_user(new_password, email)
+        if status: 
+            print("Password changed")
+            return jsonify({}), 204  # "Password has been changed!"  
+        else:
+            return jsonify({}), 500 # "Password has not been changed"
+    else:
+        return jsonify({}), 400 # "Old password is incorrect"
+
+
+@app.route("/myServer/getDataByToken", methods=['GET'])
+def get_user_data_by_token():
+    """Verify current user through token and attemp to return the data of the user"""
+    token = request.headers["Authorization"]
+
+    # Validate token
+    if token not in session:
+        return jsonify({}), 401  # "User not signed in or invalid access token"
+
+    # Extracting the email of the current user
+    email = session[token][0]
+    return get_user_data_by_email(email)
+
+
+@app.route("/myServer/getDataByEmail/<email>", methods=['GET'])
+def get_user_data_by_email(email):
+    """Get user data by email"""
+    token = request.headers["Authorization"]
+
+    # Validate Token 
+    tmp =  token_has_error(token)
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+
+    # Validate email
+    if email is None:
+        return True, 400   
+    if len(email) > 50:   
+        return True, 400
+
+    # Attempting to find the data of the current user in the database
+    data = database_helper.find_user(email)
+    if data is None or data == []:
+        return jsonify({}), 404 #"No user found by your destination email"
+
+    formated_data = {"email": data[0], "firstname": data[2], "familyname": data[3], "gender": data[4], "city": data[5], "country": data[6]}
+    return jsonify({"data" : formated_data}), 200 # "Data successfully sent to you!"
+
+
+@app.route("/myServer/getUserMessageByToken", methods=['GET'])
+def get_user_messages_by_token():
+    """Get user's message wall thought the token of the user"""
+    token = request.headers["Authorization"]
+
+    # Validate Token 
+    tmp = token_has_error(token)
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+
+    # Extracting the email of the current user 
+    email = session[token][0]
+    return get_user_messages_by_email(email)
+
+@app.route("/myServer/getMessagesByEmail/<req_email>", methods=['GET'])
+def get_user_messages_by_email(req_email):
+    """Get user's message wall thought the email of the user"""
+    token = request.headers["Authorization"]
+
+    # Validate Token 
+    tmp = token_has_error(token)
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    
+    # Validate email
+    if req_email is None:
+        return True, 400   
+    if len(req_email) > 50:   
+        return True, 400
+
+    # Find requested user in the data base
+    rows = database_helper.find_user(req_email) 
+
+    # Error check
+    if rows is None or rows == []:     
+        return jsonify({}), 404 #"No user found by your destination email"
+
+    # Insert post-info into array
+    rows = database_helper.get_post(req_email)
+    result = []
+    for row in rows:
+         result.append({"email": row[0], "person_who_posted": row[1], "message": row[2]})
+
+    # Notify user if the wall is empty or not, and if not, return the all messages
+    if result == []:
+        return jsonify({}), 204 #"user's wall had no messages to collect"
+    return jsonify({"data" : result}), 200 # User posts has been displayed"
+
+
+@app.route("/myServer/post", methods=['POST'])
+def post_message():
+    """Post a message on sombody's wall"""
+
+    # Find out sender's email
+    token = request.headers["Authorization"] 
+    tmp = token_has_error(token)
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+
+    # Extracting the email of the current user
+    my_email = session[token][0]
+    
+    # Find out & check email we are posting to
+    tmp = input_has_error('email')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    destination_email = tmp[2]
+
+    # Finding out if the user exist, who we wanna write a message to
+    rows = database_helper.find_user(destination_email) 
+    if rows is None or rows == []:
+        return jsonify({}), 404 #"No user found by your destination email"
+
+    # Verify message that we want to post
+    tmp = input_has_error('message')
+    if tmp[0]:
+        return jsonify({}), tmp[1]
+    message = tmp[2]
+
+    # Calling and error checking function
+    if not database_helper.create_post(my_email, destination_email, message):
+        return jsonify({}), 500 #"Server failed to post message to database"
+    return jsonify({}), 204 #"Succeeded to post message")
+
+
+if __name__ == '__main__':
+    # app.run(port=5000, debug=True)
+    app.debug = True
+    http_server = WSGIServer(('127.0.0.1', 5000), app)
+    http_server.serve_forever()
diff --git a/Lab_4/Twidder/static/.DS_Store b/Lab_4/Twidder/static/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..36d83147cae8a46f11b3a295805bf360ebe1a510
Binary files /dev/null and b/Lab_4/Twidder/static/.DS_Store differ
diff --git a/Lab_4/Twidder/static/client.css b/Lab_4/Twidder/static/client.css
new file mode 100644
index 0000000000000000000000000000000000000000..ed4d8f6a197114a38324290aac70379ad5f9ce3c
--- /dev/null
+++ b/Lab_4/Twidder/static/client.css
@@ -0,0 +1,274 @@
+*{
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+}
+#windowDiv{
+    background-color:rgb(0, 195, 255);
+    position: absolute;
+    width: 100%;
+    height: 100%;  
+}
+
+/* - - - - - Profile View - - - - - */
+
+#big_div{
+    width: 600px;
+    height: 400px;
+    margin: auto;
+    top: 100px; 
+    position: relative;
+}
+
+img {
+    width:340px;
+    height:400px;
+    border:1px solid rgb(5, 6, 7);
+}
+
+#rhs_div{
+    float:right;
+    height:100%;
+    width:250px;
+}
+
+#login_div {
+    background-color: white;
+    text-align:left;
+    height: 100px;
+    width:100%;
+    border:1px solid rgb(5, 6, 7);
+    padding-top: 20px;
+    margin-bottom: 10px;
+}
+
+#signup_div {
+    background-color: white;
+    padding-top: 20px;
+    text-align:left;
+    height: 290px;
+    width:100%;
+    border:1px solid rgb(5, 6, 7);
+}
+
+#view_div{
+    text-align:center;
+    height:370px;
+    width:570px;
+    border:10px solid rgb(5, 6, 7);
+    padding:5px;
+    margin:auto;
+}
+form{
+    padding: 5px;
+}
+input{
+    float: right;
+}
+select{
+    float: right;
+    width: 60%;
+}
+label{
+    display: inline-block;
+    height: 26px;
+}
+
+#AccountPage label{
+    margin: 10px;
+}
+
+#match_error {
+    color: rgb(241, 4, 4);
+    position: absolute;
+    top: 230px;
+    left: 10px;
+    display: none;
+}
+
+#old_error{
+    color: rgb(255, 0, 0);
+    position: absolute;
+    top: 230px;
+    display: none;
+}
+
+#AccountPage input{
+   margin-top: 10px; 
+   margin-right: 67%;
+}
+
+#AccountPage label{
+    margin: 10px;
+}
+
+
+h1, h3{
+    text-align: center;
+}
+
+#error{
+    bottom: 20px;
+    left: 4px;
+    color: rgb(252, 6, 170);
+    position: relative;
+    font-size: 12px;
+}
+#signUpError{
+    bottom: 36px;
+    left:31px;
+    color: rgb(252, 6, 170);
+    position: relative;
+    font-size: 12px;
+}
+
+#signup_div h3{
+    position:relative;
+    bottom:10px;
+}
+
+/* - - - - - Profile View - - - - - */
+
+.tabcontent button, .reloadButton{
+    font-size: 15px;
+    position: absolute;
+    padding: 5px;
+    margin: 30px;
+}
+#signOutButton{
+    right: 0px;
+    bottom: 0px;
+}
+#confirmButton{
+    right: 60%;
+    bottom: 50%;
+}
+.textAreaButton {
+    left: 0px;
+    bottom: 0px;
+}
+
+#tabDiv {
+    top: 100px;
+    position: relative;
+    margin: auto;
+    width: 1000px;
+    height: 50px;
+}
+.tab{
+    background-color: red;
+    display: inline-block;
+    height: 100%;
+    width: 25%;
+    border-top-left-radius: 12px;
+    border-top-right-radius: 12px;
+    text-align: center;
+    padding:10px;
+    font-size: 25px;
+}
+#Home{
+    background-color: lightgreen;
+}
+
+.tabcontent{
+    display: none;
+    top: 100px;
+    position: relative;
+    margin: auto;
+    width: 1000px;
+    height: 500px;
+    background-color: lightgreen;
+}
+
+.tabcontent p{
+    margin:10px;
+}
+
+#HomePage{
+    display: Block;
+}
+
+.tabcontent h1{
+    padding: 10px;
+    position: relative;
+}
+.tabcontent h3{
+    text-align: left;
+    left: 24px;
+    position: relative;
+    display: inline;
+    width: 50%;
+}
+.tabcontent h3.nr2{
+    left: 50%;
+    position: absolute;
+}
+.userInfo{
+    margin: 24px;
+    background-color: white;
+    width: 40%;
+}
+.inputTextArea{
+    margin: 18px;
+}
+th, td{
+    text-align: left;
+    padding: 4px;
+}
+.reloadButton{
+    right: 0px;
+    bottom: 0px; 
+}
+
+
+.PostedMessagesDiv{
+    background-color: white;
+    position: absolute;
+    width: calc(50% - 14px);
+    height: 316px;
+    right: 24px;
+    top: 103px;
+   
+    /* Get a Schrollbar */
+    overflow: auto;
+}
+
+#searchLabel{
+    left: 305px;
+    position: absolute;
+}
+
+#searchInput {
+    left: 489px;
+    position: absolute;
+    width: 240px;
+}
+#searchButton{
+    left: 740px;
+    position: absolute;
+}
+
+#searchMessage{
+    color:red;
+    left: 478px;
+    top:420px;
+    position: absolute;
+}
+
+/* #BrowsePage p{
+    color:red;
+    left: 600px;
+    top:440px;
+    position: absolute;
+} */
+
+
+/* #repeat_pass{
+    background-color: rgb(197, 4, 235);
+    text-align:center;
+    width: 600px;
+    margin: auto;
+    height: 60px;
+    position:relative; top:90px;
+    border: 1px solid black;
+} */
\ No newline at end of file
diff --git a/Lab_4/Twidder/static/client.html b/Lab_4/Twidder/static/client.html
new file mode 100644
index 0000000000000000000000000000000000000000..7834b2f76a8137d4b6ef69e7ec94a4a722cdac9b
--- /dev/null
+++ b/Lab_4/Twidder/static/client.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>Twidder</title>
+        <meta charset="utf-8">
+        <link href="static/client.css" type="text/css" rel="stylesheet">
+        <script src="static/client.js"></script>
+        <!-- <script src="static/serverstub.js"></script> -->
+        <!-- <script src="server.py"></script> -->
+
+        <script type="text/view" id="welcomeview">    
+            <div id="repeat_pass"></div>
+            <div id="big_div">
+                <img src="static/wimage.png">
+                <div id="rhs_div">
+                    <div id="login_div">
+                        <form id="sign_in_form" action ="" method="" onsubmit="return sign_in()">
+                            <label for="Email_login">Email </label>
+                            <input type="Email" name="name" id="Email_login" size="15" required></br>
+                            <label for="Password_login">Password </label>
+                            <input type="password" name="name" id="Password_login" size="15" required></br>
+                            <input type="submit" value="login"></br>
+                        </form>
+                        <p id="error"> </p>
+                    </div>
+    
+                    <div id="signup_div">
+                        <form id="signup_form" action="" method="" onsubmit="return sign_up()">
+                            <h3>Sign up here</h3>
+    
+                            <label for="First name">First name</label><input type="text" name="name" id="First name" size = "15" required> </br>
+                            <label for="Family name">Family name</label><input type="text" name="name" id="Family name" size = "15" required> </br>
+                            <label for="Gender">Gender</label>
+                            <select id="Gender" name="Gender" required>
+                                <option value="-" ></option>
+                                <option value="Man" >Man</option>
+                                <option value="Female" >Female</option>
+                            </select><br>
+                        
+                            <label for="City">City </label><input type="text" name="name" id="City" size="15" required> </br>
+                            <label for="Country">Country </label><input type="text" name="name" id="Country" size="15" required> </br>
+                            <label for="Email_sign_up">Email </label><input type="Email" name="name" id="Email_sign_up" size="15" required> </br>
+
+                            <label for="Password_sign_up">Password </label><input type="password" name="name" id="Password_sign_up" size="15" required> </br>
+
+                            <label for="Repeat PSW">Repeat PSW </label><input type="password" name="name" id="Repeat PSW" size="15" required> </br>
+                            <input type="submit" value="Sign Up"></br></br>
+                            
+                        </form>
+                        <p id="signUpError"> </p>
+                    </div>
+                </div>
+            </div>
+        </script>
+
+        <script type="text/view" id="profileview">
+            <div id="tabDiv">          
+                <div id="Home" class="tab" onclick= "showPage(event, 'HomePage')" >Home</div>
+                <div class="tab" onclick= "showPage(event, 'BrowsePage')">Browse</div>
+                <div class="tab"onclick= "showPage(event, 'AccountPage')">Account</div>
+            </div>
+            <div id="HomePage" class="tabcontent">
+                <h1>Homepage</h1>
+                <h3>User Details:</h3>
+                <h3 class="nr2">Posted Messages:</h3>
+                <table id=homeUserInfo class=userInfo>
+                    <tr>
+                        <th>Email:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Name:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Family name:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Gender:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>City:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Country:</th>
+                        <td></td>
+                    </tr>
+                </table>
+                <form id="homeInputTextArea" class="inputTextArea" onsubmit="return updateWall('home')">
+                    <label for="PostMessage">Post a message on your wall:</label> <br>
+                    <textarea id="homeTextarea" name="textarea" rows="6" cols="50"></textarea>                    
+                    <button class="textAreaButton" type="submit" value="Submit">Submit</button>
+                </form>
+                <div id="homePostedMessagesDiv" class="PostedMessagesDiv"></div>
+                <form id="homeReloadForm" class="reloadForm" onsubmit="return reloadWall('home')">
+                    <input id="homeReloadButton" class="reloadButton" type="submit" value="Reload">
+                </form>
+
+            </div>
+            <div id="BrowsePage" class="tabcontent">
+                <h1>Browsepage</h1>
+                <h3>User Details:</h3>
+                <h3 class="nr2">Posted Messages:</h3>
+                <table id=browseUserInfo class=userInfo>
+                    <tr>
+                        <th>Email:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Name:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Family name:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Gender:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>City:</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>Country:</th>
+                        <td></td>
+                    </tr>
+                </table>
+
+                <form id="browseInputTextArea" class="inputTextArea" onsubmit="return updateWall('browse')">
+                    <label for="PostMessage">Post a message on your wall:</label> <br>
+                    <textarea id="browseTextarea" name="textarea" rows="6" cols="50"></textarea>                    
+                    <button class="textAreaButton" type="submit" value="Submit">Submit</button>
+                </form>
+
+                <div id="browsePostedMessagesDiv" class="PostedMessagesDiv"></div>
+                <form id="browseReloadForm" class="reloadForm" onsubmit="return reloadWall('browse')">
+                    <input id="browseReloadButton" class="reloadButton" type="submit" value="Reload">
+                </form>
+
+                <form id="searchForm" onsubmit="return searchAnotherUser(event)">
+                    <label id="searchLabel" for="searchUser">Search by user email: </label>
+                    <input id="searchInput" type="searchedEmail" name="searchedEmail">
+                    <input id="searchButton" type="submit" value="Search!">
+                </form>
+
+                <p id = searchMessage>  </p>
+
+            </div>
+            <div id="AccountPage" class="tabcontent">
+                <h1>Accountpage</h1>
+                <label for="Change Password">Old password:</label>
+                <input type="password" name="name" id="old_password" size="15" required> 
+                <label for="Change Password">New Password:</label>
+                <input type="password" name="name" id="new_password" size="15" required> 
+                <label for="Confirm new password"> Confirm new password: </label>
+                <input type="password" name="name" id="confirm_pass" size="15" required> 
+
+                <error id=match_error> New password and confirm password does not match </error>
+                <p id = old_error> Old password is incorrect </p>
+
+                <button id=confirmButton type="submit" name="submit" onclick="change_password(event)">Confirm</button>
+                <button id="signOutButton" type="submit" name="submit" onclick="sign_out()">Sign Out</button>
+            </div>
+        </script>
+
+    </head>
+
+    <body>
+        <div id="windowDiv"></div>
+    </body>
+    
+</html>
+
diff --git a/Lab_4/Twidder/static/client.js b/Lab_4/Twidder/static/client.js
new file mode 100644
index 0000000000000000000000000000000000000000..a4995c8c8e36ea8091923a9d74a35ceb0cb90c13
--- /dev/null
+++ b/Lab_4/Twidder/static/client.js
@@ -0,0 +1,607 @@
+// Global variables
+var minPassLength = 2;
+var windowDiv;
+var welcomeDiv;
+var profileDiv;
+var url = 'http://' + document.domain + ':5000/myServer/';
+var curr_page = "";
+var socket;
+
+// source "/Users/lorenzo/OneDrive - Linköpings universitet/Skola/DI3B/TDDD97/virtualenv/bin/activate"
+
+
+function connectWithSocket() {
+    let token = localStorage.getItem("currentUser");
+            
+    // Changes the view to profile view and loads user info
+    displayView();
+    setUserDetails("home");
+
+
+    // Establish web socket
+    socket = new WebSocket('ws://' + document.domain + ':5000/myServer/api');
+
+    socket.onopen = function (event) {
+        let myEmail = localStorage.getItem("homeEmail");
+
+        // Todo - ändra så att token skickas i header istället
+        this.send(JSON.stringify({token: token, email: myEmail}));
+        console.log("Nu har jag skickat");
+    }
+
+    socket.onmessage = function (event) {
+        let response = JSON.parse(event.data);
+
+        console.log("Nu fick jag svar"); 
+        switch (response["action"]) {
+            case "signOut":
+
+                // If old socket open, close it.
+                socket.close();
+                console.log(response);
+
+                // Reset token in the localStorage
+                localStorage.setItem("currentUser", "");
+                localStorage.setItem("homeEmail", "");
+                localStorage.setItem("browseEmail", "");
+
+                // Changes the view to welcome view
+                displayView();
+                document.getElementById("error").innerHTML = "Signed Out, you signed in elsewhere";
+                break;
+            case "signIn":
+                console.log(response);
+                console.log("ja är signed in");
+                break;
+        } 
+    }
+}
+
+
+// - - - - - Init Request Objects - - - - - //
+
+// Sign In Request Object
+var signInRequest = new XMLHttpRequest();
+signInRequest.onreadystatechange = function() {
+    if (this.readyState == 4) {
+
+        let errorMessage = document.getElementById("error");
+        if (this.status == 204) {
+            let token = this.getResponseHeader("Authorization")
+            localStorage.setItem("currentUser", token);
+            connectWithSocket();
+        }
+        else if (this.status == 400) {
+            errorMessage.innerHTML = "Error 400: Incorrect format"; 
+        }
+        else if (this.status == 404) {
+            errorMessage.innerHTML = "Error 404: No user with that email exists"; 
+        }
+        else if (this.status == 401) {
+            errorMessage.innerHTML = "Error 401: Incorrect password"; 
+        }
+        else {
+            errorMessage.innerHTML = "Unknown error"; 
+        } 
+    }
+};
+
+// Sign Out Request Object
+var signOutRequest = new XMLHttpRequest();
+signOutRequest.onreadystatechange = function() {
+    if (this.readyState == 4) {
+        if (this.status == 204) {
+
+            // Reset token in the localStorage
+            localStorage.setItem("currentUser", "");
+            localStorage.setItem("homeEmail", "");
+            localStorage.setItem("browseEmail", "");
+
+            // Changes the view to welcome view
+            displayView();
+        }
+        else if (this.status == 401) {
+            console.log("Error 401: You are not logged in");
+            displayView();
+        }
+        else {
+            console.log("Unknown error");
+            displayView();
+        } 
+    }
+};
+
+// Sign Up Request Object
+var signUpRequest = new XMLHttpRequest();
+signUpRequest.onreadystatechange = function () {
+    if (this.readyState == 4) {
+
+        let errorMessage = document.getElementById("error");
+        if (this.status == 204) {
+            errorMessage.innerHTML = "You have signed up";
+        }
+        else if (this.status == 400) {
+            errorMessage.innerHTML = "Error 400: Enter the data in the correct format";
+        }
+        else if (this.status == 409) {
+            errorMessage.innerHTML = "Error 409: User already exists";
+        }
+        else if (this.status == 500) {
+            errorMessage.innerHTML = "Error 500: Error at server side";
+            }
+        else {
+            errorMessage.innerHTML = "Unknown error";
+        }
+    }
+};
+
+// Change Password Request Object
+var ChangePassRequest = new XMLHttpRequest();
+ChangePassRequest.onreadystatechange = function () {
+    if (this.readyState == 4) {
+
+        let errorMessage = document.getElementById("match_error");
+        if (this.status == 204) {
+            errorMessage.innerHTML = "Password has been changed!";
+        }
+        else if (this.status == 400) {
+            errorMessage.innerHTML = "Error 400: Old password is incorrect or Old/New password has wrong format"
+        }
+        else if (this.status == 401) {
+            errorMessage.innerHTML = "Error 401: You are not logged in"
+        }
+        else if (this.status == 500) {
+            errorMessage.innerHTML = "Error 500: Error at server side";
+        }
+        else {
+            errorMessage.innerHTML = "Unknown error"
+        }
+        errorMessage.style.display = "block";
+    }
+};
+
+// Get user data by token Request Object
+var dataByTokenRequest = new XMLHttpRequest();
+dataByTokenRequest.onreadystatechange = function() {
+    if (this.readyState == 4) {
+        if (this.status == 200) {
+            
+            let userDataArray = JSON.parse(this.responseText);
+
+            // Putting the user data we retrieved from server to the corresponding html table
+            let userTable = document.getElementById("homeUserInfo");
+            userTable.rows[0].cells[1].innerHTML = userDataArray["data"].email;
+            userTable.rows[1].cells[1].innerHTML = userDataArray["data"].firstname;
+            userTable.rows[2].cells[1].innerHTML = userDataArray["data"].familyname;
+            userTable.rows[3].cells[1].innerHTML = userDataArray["data"].gender;
+            userTable.rows[4].cells[1].innerHTML = userDataArray["data"].city;
+            userTable.rows[5].cells[1].innerHTML = userDataArray["data"].country;
+
+            localStorage.setItem("homeEmail", userDataArray["data"].email);
+            reloadWall("home");
+        }
+        else if (this.status == 401) {
+            console.log("Error 401: You are not logged in");
+        }
+        else if (this.status == 400) {
+            console.log("Error 400: Incorrect format");
+        }
+        else if (this.status == 404) {
+            console.log("Error 404: No user with that email exists");
+        }
+        else {
+            console.log("Unknown error");
+        }
+    }
+};
+
+// Get user data by email Request Object
+var dataByEmailRequest = new XMLHttpRequest();
+dataByEmailRequest.onreadystatechange = function() {
+    if (this.readyState == 4) {
+
+        let errorMessage = document.getElementById("searchMessage");
+        if (this.status == 200) {
+           
+            let userDataArray = JSON.parse(this.responseText);
+
+            // Putting the user data we retrieved from server to the corresponding html table
+            let userTable = document.getElementById("browseUserInfo");
+            userTable.rows[0].cells[1].innerHTML = userDataArray["data"].email;
+            userTable.rows[1].cells[1].innerHTML = userDataArray["data"].firstname;
+            userTable.rows[2].cells[1].innerHTML = userDataArray["data"].familyname;
+            userTable.rows[3].cells[1].innerHTML = userDataArray["data"].gender;
+            userTable.rows[4].cells[1].innerHTML = userDataArray["data"].city;
+            userTable.rows[5].cells[1].innerHTML = userDataArray["data"].country;
+
+            localStorage.setItem("browseEmail", userDataArray["data"].email);
+            errorMessage.iinnerHTML = ""
+            reloadWall("browse");
+        }
+        else {
+            if (this.status == 401) {
+                errorMessage.innerHTML = "Error 401: You are not loged in"; 
+            }
+            else if (this.status == 400) {
+                errorMessage.innerHTML = "Error 400: Incorrect format"; 
+            }
+            else if (this.status == 404) {
+                errorMessage.innerHTML = "Error 404: No user with that email exists"; 
+            }
+            else {
+                errorMessage.innerHTML = "Unknown error"; 
+            } 
+            localStorage.setItem("browseEmail", "");
+        }
+    }
+};
+
+// Get Messages By Token Request Object
+var messagesByTokenRequest = new XMLHttpRequest();
+messagesByTokenRequest.onreadystatechange = function() {
+    if (this.readyState == 4) {
+
+        if (this.status == 200) {
+           
+            let currentWall = JSON.parse(this.responseText);
+
+            // Formating the text before putting to wall
+            let complete = "";
+            for (let i = 0; i < currentWall.data.length; i++) {
+                complete += currentWall.data[i].person_who_posted + ':   ' + currentWall.data[i].message + "</br>";
+            }
+            document.getElementById('homePostedMessagesDiv').innerHTML = complete;
+        }
+        else if (this.status == 204) {
+            console.log("Success, but no messages to receive");
+        }
+        else if (this.status == 401) {
+            console.log("Error 401: You are not loged in");
+        }
+        else if (this.status == 400) {
+            console.log("Error 400: Incorrect format");
+        }
+        else if (this.status == 404) {
+            console.log("Error 404: No user with that email exists");
+        }
+        else {
+            console.log("Unknown error");
+        } 
+    }
+};
+
+// Get Messages By Email Request Object
+var messagesByEmailRequest = new XMLHttpRequest();
+messagesByEmailRequest.onreadystatechange = function() {
+    if (this.readyState == 4) {
+
+        if (this.status == 200) {
+           
+            let currentWall = JSON.parse(this.responseText);
+
+            // Fromating the text before putting to wall
+            let complete = "";
+            for (let i = 0; i < currentWall.data.length; i++) {
+                complete += currentWall.data[i].person_who_posted + ':   ' + currentWall.data[i].message + "</br>";
+            }
+            document.getElementById('browsePostedMessagesDiv').innerHTML = complete;
+
+        }
+        else if (this.status == 204) {
+            console.log("Success, but no messages to receive");
+        }
+        else if (this.status == 401) {
+            console.log("Error 401: You are not loged in");
+        }
+        else if (this.status == 400) {
+            console.log("Error 400: Incorrect format");
+        }
+        else if (this.status == 404) {
+            console.log("Error 404: No user with that email exists");
+        }
+        else {
+            console.log("Unknown error");
+        } 
+    }
+};
+
+// Post Message Request Object
+var postMessageRequest = new XMLHttpRequest();
+postMessageRequest.onreadystatechange = function () {
+    if (this.readyState == 4) {
+
+        let errorMessage = document.getElementById('searchMessage');
+        if (this.status == 204) {   
+            console.log('Message posted!')
+            errorMessage.innerHTML = "";
+            if (curr_page != null && curr_page != "")
+                reloadWall(curr_page);
+        }
+        else if (this.status == 400) {
+            errorMessage.innerHTML = "Error 400: Incorrect format";
+        }
+        else if (this.status == 404) {
+            errorMessage.innerHTML = "Error 404: No user selected to write to";
+        }
+        else if (this.status == 401) {
+            errorMessage.innerHTML = "Error 401: You are not loged in";
+        }
+        else if (this.status == 500) {
+            errorMessage.innerHTML = "Error 500: Error at server side";
+        }
+        else {
+            errorMessage.innerHTML = "Unknown error";
+        }
+    }
+};
+
+
+// - - - - - Init functions - - - - - //
+
+// Function for displaying the right view
+displayView = function(){
+    if (localStorage.getItem("currentUser") == "")
+        windowDiv.innerHTML = welcomeDiv.innerHTML;
+    else
+        windowDiv.innerHTML = profileDiv.innerHTML;
+};
+
+
+// - - - - - The function when page loads - - - - - //
+
+window.onload = function () {
+
+    // Delete when done - används för att hoppa tillbaka till rtt view under utveckling
+    // localStorage.setItem("currentUser", "");
+    // localStorage.setItem("homeEmail", "");
+    // localStorage.setItem("browseEmail", "");
+
+    // All key/value pairs stored in out localStorage
+    // If first time? check this:
+    if (localStorage.getItem("homeEmail") == null)
+        localStorage.setItem("homeEmail", "");
+    if (localStorage.getItem("browseEmail") == null)
+        localStorage.setItem("browseEmail", "");
+    if (localStorage.getItem("currentUser") == null)
+        localStorage.setItem("currentUser", "");
+    
+    // Initialize div objects (global variables)
+    welcomeDiv = document.getElementById("welcomeview");
+    profileDiv = document.getElementById("profileview");
+    windowDiv = document.getElementById("windowDiv");
+    
+    if (localStorage.getItem("currentUser") != "") {//if logged in
+        connectWithSocket();
+    }
+    else {
+        // Set correct view depending on if someone is signed in or not
+        displayView();
+    }
+};
+
+
+// Function for setting user details, either for yourself or for some other user
+function setUserDetails(who) {
+
+    // "who" could be either home or browse, this way know which info we are displaying and where
+    let token = localStorage.getItem("currentUser");
+    if (token != null && token != "" ){
+        let userTable = document.getElementById(who + "UserInfo");
+        let email = localStorage.getItem(who + "Email");
+
+        // Display my own info
+        if (who == 'home') {
+            // Send request to server about receiving the info
+            dataByTokenRequest.open("GET", url + "getDataByToken", true);
+            dataByTokenRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+            dataByTokenRequest.setRequestHeader("Authorization", token)
+            dataByTokenRequest.send();
+        }
+
+        // Display the info about the searched user
+        else if (who == 'browse' && email != null && email != "") {
+            // Send request to server about receiving the info
+            dataByEmailRequest.open("GET", url + "getDataByEmail/" + email, true);
+            dataByEmailRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+            dataByEmailRequest.setRequestHeader("Authorization", token)
+            dataByEmailRequest.send();
+        }
+    }
+    return false;
+}
+
+// - - - - - Sign Up/In/Out - - - - - //
+
+function sign_up() {
+
+    let form = document.getElementById("signup_form");
+    let errorMess = document.getElementById("signUpError");
+    let user = {
+        firstname: form[0].value,
+        familyname: form[1].value,
+        gender: form[2].value,
+        city: form[3].value,
+        country: form[4].value,
+        email: form[5].value,
+        password: form[6].value,
+        repeat_password: form[7].value
+    };
+
+
+    // Error checks
+    if (user.password.length < minPassLength)
+        errorMess.innerHTML = "Password's length is too short";
+    else if (user.password != user.repeat_password)
+        errorMess.innerHTML = "Passwords does not match";
+    else {
+
+        // Sending sign_ou request to "server"
+        signUpRequest.open("POST", url + "sign_up", true);
+        signUpRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+        signUpRequest.send(JSON.stringify(user));
+    }
+    return false;
+}
+
+function sign_in() {
+    let form = document.getElementById("sign_in_form");
+    let email = form[0].value;
+    let password = form[1].value;
+    let errorMess = document.getElementById("error");
+  
+    // Error checks
+    if (password.length < minPassLength)
+        errorMess.innerHTML = "Password's length is too short";
+    else {
+
+        // Sending sign_in request to "server"
+        signInRequest.open("POST", url + "sign_in", true);
+        signInRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+        signInRequest.send(JSON.stringify({email: email,password: password}));
+    }
+    return false;
+}
+
+function sign_out(){
+    let token = localStorage.getItem("currentUser");
+
+    // Sending sign_out request to "server"
+    signOutRequest.open("POST", url + "sign_out", true);
+    signOutRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+    signOutRequest.setRequestHeader("Authorization", token)
+    signOutRequest.send();
+
+    return false;
+}
+
+
+
+// - - - - - Changing tabs - - - - - //
+
+function showPage(ourEvent, name) {
+
+    // - - Fixing the tabs - - //
+    // Unselect all
+    let tabcontent = document.getElementById("tabDiv");
+    for (i = 0; i < tabcontent.children.length; i++) {
+        tabcontent.children[i].style.backgroundColor = "red";
+    }
+    // Select only the one we want
+    let div = ourEvent.target;
+    div.style.backgroundColor = "lightgreen";
+
+
+    // - - Fixing the content - - //
+    // Unselect all
+    let allPages = document.getElementsByClassName("tabcontent");
+    for (let i = 0; i < allPages.length; i++) {
+        allPages[i].style.display = "none";
+    }
+    // Select only the one we want
+    let selected_tab = document.getElementById(name);
+    selected_tab.style.display = "block";
+
+    return false;
+}
+
+
+
+// - - - - - Changing Password - - - - - //
+
+//Function for changing password
+function change_password(ourEvent){
+    let old_pass = document.getElementById("old_password").value;
+    let new_pass = document.getElementById("new_password").value;
+    let confirm_pass = document.getElementById("confirm_pass").value;
+    let token = localStorage.getItem("currentUser");
+    let error_text = document.getElementById("match_error");
+
+    // New password have to be entered twice, otherwise throw error
+    if (new_pass.length < minPassLength) {
+        error_text.innerHTML = "New password too short";
+    }
+    else if (new_pass != confirm_pass) {
+        error_text.innerHTML = "New password and confirm password does not match";
+    }
+    else {
+        ChangePassRequest.open("PUT", url + "change_password", true);
+        ChangePassRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+        ChangePassRequest.setRequestHeader("Authorization", token)
+        ChangePassRequest.send(JSON.stringify({old_password:old_pass, new_password:new_pass}));
+    }
+    return false;
+}
+
+// - - - - - Changing the wall - - - - - //
+
+// Submit text button
+function updateWall(who){
+    // "who" could be either home or browse, this way know which wall to write on 
+    let messageToWall = document.getElementById(who + 'Textarea').value;
+    let errorMessage = document.getElementById('searchMessage');
+    let token = localStorage.getItem("currentUser");
+    let email = localStorage.getItem(who + "Email");
+    curr_page = who;
+
+    if (email != null && email != "" && token != null && token != "") {
+
+        // Send user's message to the server
+        postMessageRequest.open("POST", url + "post", true);
+        postMessageRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+        postMessageRequest.setRequestHeader("Authorization", token)
+        postMessageRequest.send(JSON.stringify({ email: email, message: messageToWall }));
+
+        errorMessage.innerHTML = "";
+    }
+    else{
+        errorMessage.innerHTML = "No user selected to write to";
+    }
+
+    // Clear the text area the user just wrote in
+    document.getElementById(who + 'Textarea').value = "";
+    return false;
+}
+
+// Reload button
+function reloadWall(who) {
+    // "who" could be either home or browse, this way know which wall to load to 
+    let token = localStorage.getItem("currentUser");
+    let email = localStorage.getItem(who + "Email");
+
+    // If wanting messages from my own wall
+    if (who == 'home') {
+        // Send request to server about receiving the info
+        messagesByTokenRequest.open("GET", url + "getUserMessageByToken", true);
+        messagesByTokenRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+        messagesByTokenRequest.setRequestHeader("Authorization", token)
+        messagesByTokenRequest.send();
+    }
+
+    // If wanting messages from another's wall
+    else if (who == 'browse' && email != null && email != "") {
+        // Send request to server about receiving the info
+        messagesByEmailRequest.open("GET", url + "getMessagesByEmail/" + email, true);
+        messagesByEmailRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+        messagesByEmailRequest.setRequestHeader("Authorization", token)
+        messagesByEmailRequest.send();
+    }    
+
+    return false;
+}
+
+// Changing another's wall
+function searchAnotherUser(event){
+    let token = localStorage.getItem("currentUser");
+
+    // Reseting the error messages
+    document.getElementById('searchMessage').innerHTML = "";
+
+    if(token != "") {
+
+        // Store the searched email in localStorage so that it can be reached in "setUserDetails()"
+        let email = event.target["searchInput"].value;
+        localStorage.setItem("browseEmail", email);
+        setUserDetails("browse");
+    }
+    return false;
+}
\ No newline at end of file
diff --git a/Lab_4/Twidder/static/serverstub.js b/Lab_4/Twidder/static/serverstub.js
new file mode 100644
index 0000000000000000000000000000000000000000..949405c3b0f3567e1294e4b322b6909e2a322704
--- /dev/null
+++ b/Lab_4/Twidder/static/serverstub.js
@@ -0,0 +1,188 @@
+/**
+ * Serverstub.js
+ *
+ * Simple dummy server for TDDD97
+ *
+ * If you're a student, you shouldn't need to look through this file,
+ *  the description of how it works is in the lab instructions.
+ **/
+
+
+var serverstub = (function() {
+  'use strict';
+
+  var users;
+  var loggedInUsers;
+
+  var syncStorage = function(){
+	if (localStorage.getItem("users") === null) {
+	    users = {};
+	} else {
+	    users = JSON.parse(localStorage.getItem("users"));
+	}
+
+	if (localStorage.getItem("loggedinusers") === null) {
+	    loggedInUsers = {};
+	} else {
+	    loggedInUsers = JSON.parse(localStorage.getItem("loggedinusers"));
+      }
+
+  }
+  
+  var persistUsers = function(){
+    localStorage.setItem("users", JSON.stringify(users));
+  };
+  var persistLoggedInUsers = function(){
+    localStorage.setItem("loggedinusers", JSON.stringify(loggedInUsers));
+  };
+  var tokenToEmail = function(token){
+    return loggedInUsers[token];
+  };
+  var copyUser = function(user){
+    return JSON.parse(JSON.stringify(user));
+  };
+
+  var serverstub = {
+    signIn: function(email, password){
+      syncStorage();
+      if(users[email] != null && users[email].password == password){
+        var letters = "abcdefghiklmnopqrstuvwwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+        var token = "";
+        for (var i = 0 ; i < 36 ; ++i) {
+          token += letters[Math.floor(Math.random() * letters.length)];
+        }
+        loggedInUsers[token] = email;
+        persistLoggedInUsers();
+        return {"success": true, "message": "Successfully signed in.", "data": token};
+      } else {
+        return {"success": false, "message": "Wrong username or password."};
+      }
+    },
+
+    postMessage: function(token, content, toEmail){
+      syncStorage();
+      var fromEmail = tokenToEmail(token);
+      if (fromEmail != null) {
+        if (toEmail == null) {
+          toEmail = fromEmail;
+        }
+        if(users[toEmail] != null){
+          var recipient = users[toEmail];
+          var message = {"writer": fromEmail, "content": content};
+          recipient.messages.unshift(message);
+          persistUsers();
+          return {"success": true, "message": "Message posted"};
+        } else {
+          return {"success": false, "message": "No such user."};
+        }
+      } else {
+        return {"success": false, "message": "You are not signed in."};
+      }
+    },
+
+    getUserDataByToken: function(token){
+      syncStorage();
+      var email = tokenToEmail(token);
+      return serverstub.getUserDataByEmail(token, email);
+    },
+
+    getUserDataByEmail: function(token, email){
+	syncStorage();
+      if (loggedInUsers[token] != null){
+        if (users[email] != null) {
+          var match = copyUser(users[email]);
+          delete match.messages;
+          delete match.password;
+          return {"success": true, "message": "User data retrieved.", "data": match};
+        } else {
+          return {"success": false, "message": "No such user."};
+        }
+      } else {
+        return {"success": false, "message": "You are not signed in."};
+      }
+    },
+
+    getUserMessagesByToken: function(token){
+      syncStorage();
+      var email = tokenToEmail(token);
+      return serverstub.getUserMessagesByEmail(token,email);
+    },
+
+    getUserMessagesByEmail: function(token, email){
+	syncStorage();
+      if (loggedInUsers[token] != null){
+        if (users[email] != null) {
+          var match = copyUser(users[email]).messages;
+          return {"success": true, "message": "User messages retrieved.", "data": match};
+        } else {
+          return {"success": false, "message": "No such user."};
+        }
+      } else {
+        return {"success": false, "message": "You are not signed in."};
+      }
+    },
+
+    signOut: function(token){
+      syncStorage();
+      if (loggedInUsers[token] != null){
+        delete loggedInUsers[token];
+        persistLoggedInUsers();
+        return {"success": true, "message": "Successfully signed out."};
+      } else {
+        return {"success": false, "message": "You are not signed in."};
+      }
+    },
+
+    signUp: function(inputObject){ // {email, password, firstname, familyname, gender, city, country}
+      syncStorage();
+      if (users[inputObject.email] === undefined){
+        if(
+          (typeof(inputObject.email) === 'string') &&
+          (typeof(inputObject.password) === 'string') &&
+          (typeof(inputObject.firstname) === 'string') &&
+          (typeof(inputObject.familyname) === 'string') &&
+          (typeof(inputObject.gender) === 'string') &&
+          (typeof(inputObject.city) === 'string') &&
+          (typeof(inputObject.country) === 'string')
+        ) {
+          var user = {
+            'email': inputObject.email,
+            'password': inputObject.password,
+            'firstname': inputObject.firstname,
+            'familyname': inputObject.familyname,
+            'gender': inputObject.gender,
+            'city': inputObject.city,
+            'country': inputObject.country,
+            'messages': []
+          };
+            users[inputObject.email] = user;
+            persistUsers();
+            return {"success": true, "message": "Successfully created a new user."};
+        } else {
+          return {"success": false, "message": "Form data missing or incorrect type."};
+        }
+
+      } else {
+        return {"success": false, "message": "User already exists."};
+      }
+    },
+
+    changePassword: function(token, oldPassword, newPassword){
+      syncStorage();
+      if (loggedInUsers[token] != null){
+        var email = tokenToEmail(token);
+        if (users[email].password == oldPassword){
+          users[email].password = newPassword;
+          persistUsers();
+          return {"success": true, "message": "Password changed."};
+        } else {
+          return {"success": false, "message": "Wrong password."};
+        }
+      } else {
+        return {"success": false, "message": "You are not logged in."};
+      }
+    }
+  };
+
+  return serverstub;
+})();
diff --git a/Lab_4/Twidder/static/wimage.png b/Lab_4/Twidder/static/wimage.png
new file mode 100644
index 0000000000000000000000000000000000000000..048bdd2fcf666069f57f0aab8521dc49f9914725
Binary files /dev/null and b/Lab_4/Twidder/static/wimage.png differ
diff --git a/requirements.txt b/requirements.txt
index 0f7b23c1b6d9f4af401e0ea6c6ad4483f32c09d8..5d02793351f023646d3a1a828c711222ff566ebf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,15 +1,18 @@
 astroid==2.9.3
-click==8.0.3
-Flask==2.0.3
+distlib==0.3.4
+filelock==3.5.1
+gevent==21.12.0
+gevent-websocket==0.10.1
+greenlet==1.1.2
 isort==5.10.1
-itsdangerous==2.0.1
-Jinja2==3.0.3
 lazy-object-proxy==1.7.1
-MarkupSafe==2.0.1
 mccabe==0.6.1
 platformdirs==2.5.0
 pylint==2.12.2
+six==1.15.0
 toml==0.10.2
 typing_extensions==4.1.1
-Werkzeug==2.0.3
+virtualenv==20.13.1
 wrapt==1.13.3
+zope.event==4.5.0
+zope.interface==5.4.0