moved to typescript for frontend

This commit is contained in:
nova 2019-12-02 17:30:00 +05:30
parent bfe2c21e1b
commit f0e8beb27c
28 changed files with 1649 additions and 31 deletions

View File

@ -1,31 +1,32 @@
{
"dependencies": {
"alertifyjs": "^1.12.0",
"chart.js": "^2.9.3",
"dompurify": "^2.0.7",
"fuse.js": "^3.4.6",
"handlebars": "^4.5.3",
"loglevel": "^1.6.6",
"markdown-it": "^10.0.0",
"sjcl": "^1.0.8"
},
"devDependencies": {
"browserify": "^16.5.0",
"browserify-css": "^0.15.0",
"browserify-shim": "^3.8.14",
"webpack": "^4.41.2"
},
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"loglevel": "global:log",
"markdown-it": "global:markdownit",
"handlebars": "global:Handlebars",
"dompurify": "global:DOMPurify",
"fuse.js": "global:Fuse",
"sjcl": "global:sjcl"
}
"dependencies": {
"alertifyjs": "^1.12.0",
"chart.js": "^2.9.3",
"dompurify": "^2.0.7",
"fuse.js": "^3.4.6",
"handlebars": "^4.5.3",
"loglevel": "^1.6.6",
"markdown-it": "^10.0.0",
"sjcl": "^1.0.8",
"tsify": "^4.0.1"
},
"devDependencies": {
"browserify": "^16.5.0",
"browserify-css": "^0.15.0",
"browserify-shim": "^3.8.14",
"webpack": "^4.41.2"
},
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"loglevel": "global:log",
"markdown-it": "global:markdownit",
"handlebars": "global:Handlebars",
"dompurify": "global:DOMPurify",
"fuse.js": "global:Fuse",
"sjcl": "global:sjcl"
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("../model/AbstractModel");
require("../model/UserModel");
require("../view/AbstractView");
require("../view/UserView");
var UserController = /** @class */ (function () {
function UserController(model, view) {
this._model = model;
this._view = view;
}
Object.defineProperty(UserController.prototype, "model", {
/**
* Getter model
* @return {Model}
*/
get: function () {
return this._model;
},
/**
* Setter model
* @param {Model} value
*/
set: function (value) {
this._model = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(UserController.prototype, "view", {
/**
* Getter view
* @return {View}
*/
get: function () {
return this._view;
},
/**
* Setter view
* @param {View} value
*/
set: function (value) {
this._view = value;
},
enumerable: true,
configurable: true
});
return UserController;
}());
exports.UserController = UserController;

View File

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var UserModel_1 = require("./model/UserModel");
var UserView_1 = require("./view/UserView");
var UserController_1 = require("./controller/UserController");
var userModel = new UserModel_1.UserModel();
var userView = new UserView_1.UserView(userModel, 2);
userModel.attach(userView);
// userView.model
var userController = new UserController_1.UserController(userModel, userView);

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var UserModel = /** @class */ (function () {
function UserModel() {
/**
* @type {Observer[]} List of subscribers. In real life, the list of
* subscribers can be stored more comprehensively (categorized by event
* type, etc.).
*/
this.observers = [];
this.state = 0;
}
/**
* The subscription management methods.
*/
UserModel.prototype.attach = function (observer) {
console.log('Subject: Attached an observer.');
this.observers.push(observer);
};
UserModel.prototype.detach = function (observer) {
var observerIndex = this.observers.indexOf(observer);
this.observers.splice(observerIndex, 1);
console.log('Subject: Detached an observer.');
};
/**
* Trigger an update in each subscriber.
*/
UserModel.prototype.notify = function () {
console.log('Subject: Notifying observers...');
for (var _i = 0, _a = this.observers; _i < _a.length; _i++) {
var observer = _a[_i];
observer.update(this.state);
}
};
UserModel.prototype.someBusinessMethod = function () {
console.log("Subject: My state has just changed to: " + this.state);
this.notify();
};
return UserModel;
}());
exports.UserModel = UserModel;

View File

@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* The Subject owns some important state and notifies observers when the state
* changes.
*/
// class ConcreteSubject implements Subject {
// /**
// * @type {number} For the sake of simplicity, the Subject's state, essential
// * to all subscribers, is stored in this variable.
// */
// public state: number;
// /**
// * @type {Observer[]} List of subscribers. In real life, the list of
// * subscribers can be stored more comprehensively (categorized by event
// * type, etc.).
// */
// private observers: Observer[] = [];
// /**
// * The subscription management methods.
// */
// public attach(observer: Observer): void {
// console.log('Subject: Attached an observer.');
// this.observers.push(observer);
// }
// public detach(observer: Observer): void {
// const observerIndex = this.observers.indexOf(observer);
// this.observers.splice(observerIndex, 1);
// console.log('Subject: Detached an observer.');
// }
// /**
// * Trigger an update in each subscriber.
// */
// public notify(): void {
// console.log('Subject: Notifying observers...');
// for (const observer of this.observers) {
// observer.update(this);
// }
// }
// /**
// * Usually, the subscription logic is only a fraction of what a Subject can
// * really do. Subjects commonly hold some important business logic, that
// * triggers a notification method whenever something important is about to
// * happen (or after it).
// */
// public someBusinessLogic(): void {
// console.log('\nSubject: I\'m doing something important.');
// this.state = Math.floor(Math.random() * (10 + 1));
// console.log(`Subject: My state has just changed to: ${this.state}`);
// this.notify();
// }
// }
// /**
// * The client code.
// */
// const subject = new ConcreteSubject();
// const observer1 = new ConcreteObserverA();
// subject.attach(observer1);
// const observer2 = new ConcreteObserverB();
// subject.attach(observer2);
// subject.someBusinessLogic();
// subject.someBusinessLogic();
// subject.detach(observer2);
// subject.someBusinessLogic();

View File

@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// /**
// * The Observer interface declares the update method, used by subjects.
// */
// interface Observer {
// // Receive update from subject.
// update(subject: Subject): void;
// }
// /**
// * Concrete Observers react to the updates issued by the Subject they had been
// * attached to.
// */
// class ConcreteObserverA implements Observer {
// public update(subject: Subject): void {
// if (subject.state < 3) {
// console.log('ConcreteObserverA: Reacted to the event.');
// }
// }
// }
// class ConcreteObserverB implements Observer {
// public update(subject: Subject): void {
// if (subject.state === 0 || subject.state >= 2) {
// console.log('ConcreteObserverB: Reacted to the event.');
// }
// }
// }

View File

@ -0,0 +1 @@
"use strict";

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var UserView = /** @class */ (function () {
function UserView(model, element) {
this._model = model;
this._element = element;
}
Object.defineProperty(UserView.prototype, "model", {
/**
* Getter model
* @return {Model}
*/
get: function () {
return this._model;
},
/**
* Setter model
* @param {Model} value
*/
set: function (value) {
this._model = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(UserView.prototype, "element", {
/**
* Getter element
* @return {any}
*/
get: function () {
return this._element;
},
/**
* Setter element
* @param {any} value
*/
set: function (value) {
this._element = value;
},
enumerable: true,
configurable: true
});
UserView.prototype.update = function (data) {
console.log(data);
};
return UserView;
}());
exports.UserView = UserView;

View File

@ -0,0 +1,14 @@
import { Model } from "../model/AbstractModel";
import { View } from "../view/AbstractView";
// simple restrictions that MVC impose: The Control has a reference to the View and Model + the View has a reference to the Model and the Controller.It also does not have any Observer implementation in the Model, so that the View can update based on it.
// export abstract class Controller {
// protected _model: Model | undefined;
// protected _view: View | undefined;
// }
export interface Controller {
model: Model,
view: View
}

View File

@ -0,0 +1,51 @@
import { Controller } from "./AbstractController";
import "../model/AbstractModel"
import "../model/UserModel"
import "../view/AbstractView"
import "../view/UserView"
import { Model } from "../model/AbstractModel";
import { View } from "../view/AbstractView";
export class UserController implements Controller{
private _model: Model;
private _view: View;
constructor(model: Model, view: View) {
this._model = model;
this._view = view;
}
/**
* Getter model
* @return {Model}
*/
public get model(): Model {
return this._model;
}
/**
* Getter view
* @return {View}
*/
public get view(): View {
return this._view;
}
/**
* Setter model
* @param {Model} value
*/
public set model(value: Model) {
this._model = value;
}
/**
* Setter view
* @param {View} value
*/
public set view(value: View) {
this._view = value;
}
}

View File

@ -0,0 +1,41 @@
import { Controller } from "./controller/AbstractController";
import { UserModel } from "./model/UserModel"
import { Model } from "./model/AbstractModel";
import { View } from "./view/AbstractView";
import { UserView } from "./view/UserView";
import { UserController } from "./controller/UserController";
import log from "loglevel";
import { ModelFactory } from "./model/ModelFactory";
import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel";
import { ChatMessageViewModel } from "./viewmodel/ChatMessageViewModel";
import Handlebars from "handlebars";
log.setLevel("TRACE")
// let userModel = new UserModel();
const userModel = ModelFactory.createModel("USER");
const userView = new UserView(userModel, 2);
userModel.attach(userView);
// userView.model
const userController = new UserController(userModel, userView);
userModel.someBusinessMethod(5);
log.info("hello");
const chatMessageViewModel= new ChatMessageViewModel();
const activeUserViewModel = new ActiveUserViewModel();
activeUserViewModel.userName = "some user";
activeUserViewModel.lastActive = new Date();
activeUserViewModel.online = true;
function someFunc(vm: ActiveUserViewModel): void {
log.info(vm);
}
someFunc(activeUserViewModel);
// @ts-ignore: Object is possibly 'null'.
var source = document.getElementById("msg_container_template").innerHTML;
var msgContainerTemplate = Handlebars.compile(source);

View File

@ -0,0 +1,5 @@
import { Subject } from "../observe/Observable";
export interface Model extends Subject{
someBusinessMethod(data: any): void;
}

View File

@ -0,0 +1,12 @@
import { Model } from "./AbstractModel";
import { UserModel } from "./UserModel";
export class ModelFactory {
static createModel(modelName: string): Model {
switch (modelName) {
case "USER": return new UserModel();
break;
default: throw new Error("Invalid model name");
}
}
}

View File

@ -0,0 +1,48 @@
import { Subject } from "../observe/Observable";
import { Model } from "./AbstractModel";
import { Observer } from "../observe/Observer";
export class UserModel implements Model, Subject {
/**
* @type {Observer[]} List of subscribers. In real life, the list of
* subscribers can be stored more comprehensively (categorized by event
* type, etc.).
*/
private observers: Observer[] = [];
private state: number = 0;
constructor() { }
/**
* The subscription management methods.
*/
public attach(observer: Observer): void {
console.log('Subject: Attached an observer.');
this.observers.push(observer);
}
public detach(observer: Observer): void {
const observerIndex = this.observers.indexOf(observer);
this.observers.splice(observerIndex, 1);
console.log('Subject: Detached an observer.');
}
/**
* Trigger an update in each subscriber.
*/
public notify(): void {
console.log('Subject: Notifying observers...');
for (const observer of this.observers) {
observer.update(this.state);
}
}
public someBusinessMethod(num: number): void {
this.state = num;
this.helperMethod();
console.log(`Subject: My state has just changed to: ${this.state}`);
this.notify();
}
private helperMethod() {}
}

View File

@ -0,0 +1,93 @@
import { Observer } from "./Observer";
/**
* The Subject interface declares a set of methods for managing subscribers.
*/
export interface Subject {
// Attach an observer to the subject.
attach(observer: Observer): void;
// Detach an observer from the subject.
detach(observer: Observer): void;
// Notify all observers about an event.
notify(): void;
}
/**
* The Subject owns some important state and notifies observers when the state
* changes.
*/
// class ConcreteSubject implements Subject {
// /**
// * @type {number} For the sake of simplicity, the Subject's state, essential
// * to all subscribers, is stored in this variable.
// */
// public state: number;
// /**
// * @type {Observer[]} List of subscribers. In real life, the list of
// * subscribers can be stored more comprehensively (categorized by event
// * type, etc.).
// */
// private observers: Observer[] = [];
// /**
// * The subscription management methods.
// */
// public attach(observer: Observer): void {
// console.log('Subject: Attached an observer.');
// this.observers.push(observer);
// }
// public detach(observer: Observer): void {
// const observerIndex = this.observers.indexOf(observer);
// this.observers.splice(observerIndex, 1);
// console.log('Subject: Detached an observer.');
// }
// /**
// * Trigger an update in each subscriber.
// */
// public notify(): void {
// console.log('Subject: Notifying observers...');
// for (const observer of this.observers) {
// observer.update(this);
// }
// }
// /**
// * Usually, the subscription logic is only a fraction of what a Subject can
// * really do. Subjects commonly hold some important business logic, that
// * triggers a notification method whenever something important is about to
// * happen (or after it).
// */
// public someBusinessLogic(): void {
// console.log('\nSubject: I\'m doing something important.');
// this.state = Math.floor(Math.random() * (10 + 1));
// console.log(`Subject: My state has just changed to: ${this.state}`);
// this.notify();
// }
// }
// /**
// * The client code.
// */
// const subject = new ConcreteSubject();
// const observer1 = new ConcreteObserverA();
// subject.attach(observer1);
// const observer2 = new ConcreteObserverB();
// subject.attach(observer2);
// subject.someBusinessLogic();
// subject.someBusinessLogic();
// subject.detach(observer2);
// subject.someBusinessLogic();

View File

@ -0,0 +1,34 @@
import { Subject } from "./Observable";
export interface Observer {
// Receive update from subject.
update(data: any): void;
}
// /**
// * The Observer interface declares the update method, used by subjects.
// */
// interface Observer {
// // Receive update from subject.
// update(subject: Subject): void;
// }
// /**
// * Concrete Observers react to the updates issued by the Subject they had been
// * attached to.
// */
// class ConcreteObserverA implements Observer {
// public update(subject: Subject): void {
// if (subject.state < 3) {
// console.log('ConcreteObserverA: Reacted to the event.');
// }
// }
// }
// class ConcreteObserverB implements Observer {
// public update(subject: Subject): void {
// if (subject.state === 0 || subject.state >= 2) {
// console.log('ConcreteObserverB: Reacted to the event.');
// }
// }
// }

View File

@ -0,0 +1,8 @@
// simple restrictions that MVC impose: The Control has a reference to the View and Model + the View has a reference to the Model and the Controller.It also does not have any Observer implementation in the Model, so that the View can update based on it.
import { Model } from "../model/AbstractModel";
import { Controller } from "../controller/AbstractController";
import { Observer } from "../observe/Observer";
export interface View extends Observer {
model: Model,
element: any
}

View File

@ -0,0 +1,63 @@
import { Observer } from "../observe/Observer";
import { Model } from "../model/AbstractModel";
import { Subject } from "../observe/Observable";
import { View } from "./AbstractView";
import { Controller } from "../controller/AbstractController";
export class UserView implements Observer, View {
private _model: Model;
private _element: any;
constructor(model: Model, element: any) {
this._model = model;
this._element = element;
}
/**
* Getter model
* @return {Model}
*/
public get model(): Model {
return this._model;
}
/**
* Getter element
* @return {any}
*/
public get element(): any {
return this._element;
}
/**
* Setter model
* @param {Model} value
*/
public set model(value: Model) {
this._model = value;
}
/**
* Setter element
* @param {any} value
*/
public set element(value: any) {
this._element = value;
}
update(data: any): void {
this.element = data;
console.log(this.element);
}
}

View File

@ -0,0 +1,5 @@
export class ActiveUserViewModel {
userName: string | undefined;
online: boolean | undefined;
lastActive: Date | undefined;
}

View File

@ -0,0 +1,8 @@
export class ChatMessageViewModel {
public toUser: string | undefined;
public fromUser: string | undefined;
public messageCipher: any;
public messageTime!: Date;
}

View File

@ -0,0 +1,311 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script>
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" defer="defer" th:if="false"></script>
<div th:replace="fragments/head :: headFragment">
<meta charset="UTF-8">
<title id="pageTitle">Chat</title>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.8/sjcl.min.js" integrity="sha256-nIoG9XIePM1QNttI6KAGLYGNxc4DNinxxmOZW0/Z7uA=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.4.2/handlebars.min.js" integrity="sha256-oh7N5nthuhldTk8/34Za7FXv3BkeVN9vAnYk/pLfC78=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.0.3/purify.min.js" integrity="sha256-58eGKW6SunbeAY1RP9WEbg3nViB9o1qDnxV4yCITqx4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/10.0.0/markdown-it.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/alertify.min.js" integrity="sha256-yscIZgtlDR9q6eoYCRmcaiNE6W80UFSnq+6Llwu4NLI=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/css/alertify.min.css" integrity="sha256-nhstgDCuZGQRk+wvwXZIPt278arHtuZKJ1YQ0rrXiL4=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/css/themes/default.css" integrity="sha256-dawRQVhnqw8jRXaGnK0aj/NpOsPaQm+Em1sWN+fvegI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/css/themes/bootstrap.css" integrity="sha256-1fgYpB3cyITZIur7E+Mj3R54NtlN9HwHykgKTJf0pmU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.4.5/fuse.min.js" integrity="sha256-Yrh3VGzE4d9b4KANknPJAhfcKt9SgHTL0v/FrJFoPzw=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/loglevel/1.6.4/loglevel.min.js" integrity="sha256-ACTlnmNCkOooSKkPCKYbiex8WLE82aeiN+Z9ElZag5Q=" crossorigin="anonymous"></script>
<script src="./bundle.js"></script>
<link rel="stylesheet" th:href="@{/css/chat.css}" href="../../resources/static/css/chat.css">
<!-- <script th:src="@{js/my_Crypto.js}" type="text/javascript"></script> -->
</head>
<body>
<div th:include="fragments/navbar :: navbarFragment"></div>
<header id="chat-section">
<div class="dark-overlay">
<div class="chat-inner container bg-primary">
<div class="row">
<div class="col-sm d-lg-block">
<h1 class="display-4">Chat with your friends</h1>
<div class="d-flex">
<div class="p-4 align-self-start">
</div>
<div class="p-4 align-self-end">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laboriosam dolorem nostrum consequatur eos voluptates. Ipsam ullam quos illo qui. Quaerat corrupti nisi numquam rerum quasi nesciunt deserunt fugit commodi consequatur!
</div>
</div>
</div>
</div>
</div>
</div>
</header>
<div class="container-fluid h-100">
<div class="row justify-content-center h-100 mt-3">
<div class="col-md-4 col-xl-3 chat">
<div class="card mb-sm-3 mb-md-0 contacts_card">
<div class="card-header">
<form action="#" id="user-search">
<div class="input-group">
<input type="text" placeholder="Search..." id="user-search-term" class="form-control search">
<button class="search-cancel" id="user-search-cancel" hidden><i class="fas fa-times"></i></button>
<div class="input-group-prepend">
<button class="input-group-text search_btn"><i class="fas fa-search"></i></button>
</div>
</div>
</form>
</div>
<div class="card-body contacts_body">
<ui class="contacts">
<li class="active">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span class="online_icon"></span>
</div>
<div class="user_info">
<span>Khalid</span>
<p>Kalid is online</p>
</div>
</div>
</li>
</ui>
<ui class="contacts" id="contacts-box">
<th:block th:each="au: ${activeUsers}">
<li name="user-box" class="user-box">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span th:if="${au.online == true}" class="online_icon"></span>
</div>
<div class="user_info">
<span class="to-user-span" th:text="${au.userName}">Khalid</span>
<div th:switch="${au.online}">
<p th:case="true" th:text="${au.userName} + ' is online'">Khalid is online</p>
<th:block th:case="false">
<th:block th:if="${au.lastActive == null}">
<p th:text="'User has not logged in yet'"></p>
</th:block>
<th:block th:if="${au.lastActive != null}">
<p th:text="'Last active ' + ${au.lastActive}">Last active 3 hours ago</p>
</th:block>
<!-- <p th:case="${au.online == true}" th:text="${au.userName} + ' is online'">Khalid is online</p>
<p th:if="${au.online == false}" th:text="${au.userName} + ' is offline' + ' Last active = ' + ${au.lastActive}">Khalid is offline. -->
<!-- <span th:text="'Last active = ' + ${au.lastActive}"></span> -->
</th:block>
</div>
</div>
<div class="d-flex flex-column ml-auto">
<div class="text-right">Dec 25</div>
<div style="color: rgba(255,255,255,0.7);">Hello how are you</div>
</div>
</div>
</li>
</th:block>
</ui>
</div>
<div class="card-footer"></div>
</div>
</div>
<div class="col-md-8 col-xl-6 chat">
<div class="card" id="no-user-selected">
<div class="m-auto">
<div class="d-flex justify-content-center">
<div class="align-self-center">
<h2 class="display-4 no-user-selected-h2">Please select a user</h2>
<input class="form-control type_msg" size="10" type="password" id="passphrase-initial" placeholder="Passphrase " required>
</div>
</div>
</div>
</div>
<div class="card" id="chat-card" hidden>
<div class="card-header msg_head">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span class="online_icon"></span>
</div>
<div class="user_info">
<span id="user-name-span">Chat with Khalid</span>
<p id="num-messages-p">1767 Messages</p>
</div>
<div class="video_cam">
<span><i class="fas fa-video"></i></span>
<span><i class="fas fa-phone"></i></span>
</div>
</div>
<span id="action_menu_btn"><i class="fas fa-ellipsis-v"></i></span>
<div class="action_menu">
<ul>
<li><i class="fas fa-user-circle"></i> View profile</li>
<li><i class="fas fa-users"></i> Add to close friends</li>
<li><i class="fas fa-plus"></i> Add to group</li>
<li><i class="fas fa-ban"></i> Block</li>
</ul>
</div>
</div>
<div class="card-body msg_card_body" id="chat_area_new">
</div>
<div class="card-footer">
<form action="#" th:object="${chatMessageDTO}" method="post" id="chatMessageForm" class="needs-validation" novalidate>
<div class="input-group">
<div class="input-group-append">
<span class="input-group-text attach_btn"><i class="fas fa-paperclip"></i></span>
</div>
<input class="form-control type_msg" size="10" type="password" id="passphrase" placeholder="Passphrase " required>
<textarea name="" id="chatInput" class="form-control type_msg" placeholder="Type your message..." required></textarea>
<div class="input-group-append">
<button class="input-group-text send_btn"><i class="fas fa-location-arrow"></i></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<section hidden>
<div class="container">
<div class="row">
<div class="col-sm">
<h4 class="display-4 text-center py-2">Chat</h4>
<div class="card text-white bg-primary mb-3 card-form rounded mx-auto">
<div class="card-body rounded">
<!-- <h4 class="card-title">Chat</h4> -->
<div class="form-group">
<textarea id="chatTextArea" class="form-control-lg py-2" disabled></textarea>
</div>
<!-- <form action="#" th:action="@{/seedstartermng}" th:object="${seedStarter}" method="post"> -->
<!-- th:action="@{/api/chat}" -->
<div class="card-text">
</div>
<form action="#" th:object="${chatMessageDTO}" method="post" class="needs-validation" novalidate>
<div class="row">
<div class="col-3">
<div class="form-group">
<label class="lead" for="toUser">User to send to: </label>
<th:block th:each="userName: ${userNames}">
<input class="form-control" type="radio" th:field="*{toUser}" th:value="${userName}">
<label class="btn btn-secondary" th:for="${#ids.prev('toUser')}" th:text="${userName}">
Demo User
</label>
</th:block>
</div>
</div>
<div class="col">
<div class="my-form-inputs container">
<div class="form-group">
<label for="chatInput">Your message: </label>
<textarea class="form-control" type="text" required></textarea>
<div class="invalid-feedback">
Cannot be empty
</div>
</div>
<div class="form-group">
<label for="passphrase">Passphrase: </label>
<input class="form-control" type="password" required>
</div>
<div class="form-group text-center">
<button class="btn btn-secondary mx-auto">Submit</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<template id="msg_container_template">
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg">
</div>
<div class="msg_container">
{{{message}}}
<span class="msg_time">{{time}}</span>
</div>
</div>
</template>
<template id="msg_container_send_template">
<div class="d-flex justify-content-end mb-4">
<div class="msg_container_send">
{{{message}}}
<span class="msg_time_send">{{time}}</span>
</div>
<!-- <div class="img_cont_msg">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg"
class="rounded-circle user_img_msg">
</div> -->
{{{avatar}}}
</div>
</template>
<template id="user-contact-online-template">
<li name="user-box" class="user-box">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span class="online_icon"></span>
</div>
<div class="user_info">
<span class="to-user-span">{{userName}}</span>
<p>{{userName}} is online</p>
</div>
<div class="d-flex flex-column ml-auto">
<div class="text-right">Dec 25</div>
<div style="color: rgba(255,255,255,0.7);">Hello how are you</div>
</div>
</div>
</li>
</template>
<template id="user-contact-offline-template">
<li name="user-box" class="user-box">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
</div>
<div class="user_info">
<span class="to-user-span">{{userName}}</span>
<p>Last active {{lastActive}}</p>
</div>
<div class="d-flex flex-column ml-auto">
<div class="text-right">Dec 25</div>
<div style="color: rgba(255,255,255,0.7);">Hello how are you</div>
</div>
</div>
</li>
</template>
</body>
<!-- <script th:src="@{js/chat.js}" type="text/javascript"></script> -->
<script src="../static/js/bundle.js" th:src="@{/js/bundle.js}"></script>
<script src="../static/js/chatStatic.js" th:if="false"></script>
</html>
<!-- <div th:include="::frag (${value1},${value2})">...</div>
<div th:include="::frag (onevar=${value1},twovar=${value2})">...</div> -->

View File

@ -0,0 +1,63 @@
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./out", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"include": [
"src/**/*"
]
}

View File

@ -249,6 +249,11 @@ ansi-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
any-promise@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@ -805,6 +810,13 @@ constants-browserify@^1.0.0, constants-browserify@~1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
convert-source-map@^1.1.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
dependencies:
safe-buffer "~5.1.1"
convert-source-map@~1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
@ -1096,6 +1108,13 @@ errno@^0.1.3, errno@~0.1.7:
dependencies:
prr "~1.0.1"
error-ex@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
dependencies:
is-arrayish "^0.2.1"
escape-string-regexp@^1.0.3:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@ -1647,6 +1666,11 @@ is-accessor-descriptor@^1.0.0:
dependencies:
kind-of "^6.0.0"
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
is-binary-path@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
@ -1758,6 +1782,11 @@ is-regexp@^1.0.0:
resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
is-windows@^1.0.1, is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@ -2405,6 +2434,13 @@ parse-asn1@^5.0.0:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
dependencies:
error-ex "^1.2.0"
parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@ -3060,6 +3096,13 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
dependencies:
is-utf8 "^0.2.0"
strip-css-comments@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-css-comments/-/strip-css-comments-3.0.0.tgz#7a5625eff8a2b226cf8947a11254da96e13dae89"
@ -3067,7 +3110,7 @@ strip-css-comments@^3.0.0:
dependencies:
is-regexp "^1.0.0"
strip-json-comments@~2.0.1:
strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
@ -3205,6 +3248,28 @@ transformify@~0.1.1:
dependencies:
readable-stream "~1.1.9"
tsconfig@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-5.0.3.tgz#5f4278e701800967a8fc383fd19648878f2a6e3a"
integrity sha1-X0J45wGACWeo/Dg/0ZZIh48qbjo=
dependencies:
any-promise "^1.3.0"
parse-json "^2.2.0"
strip-bom "^2.0.0"
strip-json-comments "^2.0.0"
tsify@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/tsify/-/tsify-4.0.1.tgz#b19b0ddf7f184368dbf65839293d2c5a6d48453d"
integrity sha512-ClznEI+pmwY5wmD0J7HCSVERwkD+l71ch3Dqyod2JuQLEsFaiNDI+vPjaGadsuVFVvmzgoI7HghrBtWsSmCDHQ==
dependencies:
convert-source-map "^1.1.0"
fs.realpath "^1.0.0"
object-assign "^4.1.0"
semver "^5.6.0"
through2 "^2.0.0"
tsconfig "^5.0.3"
tslib@^1.9.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"

View File

@ -0,0 +1,311 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script>
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" defer="defer" th:if="false"></script>
<div th:replace="fragments/head :: headFragment">
<meta charset="UTF-8">
<title id="pageTitle">Chat</title>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.8/sjcl.min.js" integrity="sha256-nIoG9XIePM1QNttI6KAGLYGNxc4DNinxxmOZW0/Z7uA=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.4.2/handlebars.min.js" integrity="sha256-oh7N5nthuhldTk8/34Za7FXv3BkeVN9vAnYk/pLfC78=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.0.3/purify.min.js" integrity="sha256-58eGKW6SunbeAY1RP9WEbg3nViB9o1qDnxV4yCITqx4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/10.0.0/markdown-it.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/alertify.min.js" integrity="sha256-yscIZgtlDR9q6eoYCRmcaiNE6W80UFSnq+6Llwu4NLI=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/css/alertify.min.css" integrity="sha256-nhstgDCuZGQRk+wvwXZIPt278arHtuZKJ1YQ0rrXiL4=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/css/themes/default.css" integrity="sha256-dawRQVhnqw8jRXaGnK0aj/NpOsPaQm+Em1sWN+fvegI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.12.0/css/themes/bootstrap.css" integrity="sha256-1fgYpB3cyITZIur7E+Mj3R54NtlN9HwHykgKTJf0pmU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.4.5/fuse.min.js" integrity="sha256-Yrh3VGzE4d9b4KANknPJAhfcKt9SgHTL0v/FrJFoPzw=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/loglevel/1.6.4/loglevel.min.js" integrity="sha256-ACTlnmNCkOooSKkPCKYbiex8WLE82aeiN+Z9ElZag5Q=" crossorigin="anonymous"></script>
<script src="./../../javascript/ts/bundle.js" defer></script>
<link rel="stylesheet" th:href="@{/css/chat.css}" href="../../resources/static/css/chat.css">
<!-- <script th:src="@{js/my_Crypto.js}" type="text/javascript"></script> -->
</head>
<body>
<div th:include="fragments/navbar :: navbarFragment"></div>
<header id="chat-section">
<div class="dark-overlay">
<div class="chat-inner container bg-primary">
<div class="row">
<div class="col-sm d-lg-block">
<h1 class="display-4">Chat with your friends</h1>
<div class="d-flex">
<div class="p-4 align-self-start">
</div>
<div class="p-4 align-self-end">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laboriosam dolorem nostrum consequatur eos voluptates. Ipsam ullam quos illo qui. Quaerat corrupti nisi numquam rerum quasi nesciunt deserunt fugit commodi consequatur!
</div>
</div>
</div>
</div>
</div>
</div>
</header>
<div class="container-fluid h-100">
<div class="row justify-content-center h-100 mt-3">
<div class="col-md-4 col-xl-3 chat">
<div class="card mb-sm-3 mb-md-0 contacts_card">
<div class="card-header">
<form action="#" id="user-search">
<div class="input-group">
<input type="text" placeholder="Search..." id="user-search-term" class="form-control search">
<button class="search-cancel" id="user-search-cancel" hidden><i class="fas fa-times"></i></button>
<div class="input-group-prepend">
<button class="input-group-text search_btn"><i class="fas fa-search"></i></button>
</div>
</div>
</form>
</div>
<div class="card-body contacts_body">
<ui class="contacts">
<li class="active">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span class="online_icon"></span>
</div>
<div class="user_info">
<span>Khalid</span>
<p>Kalid is online</p>
</div>
</div>
</li>
</ui>
<ui class="contacts" id="contacts-box">
<th:block th:each="au: ${activeUsers}">
<li name="user-box" class="user-box">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span th:if="${au.online == true}" class="online_icon"></span>
</div>
<div class="user_info">
<span class="to-user-span" th:text="${au.userName}">Khalid</span>
<div th:switch="${au.online}">
<p th:case="true" th:text="${au.userName} + ' is online'">Khalid is online</p>
<th:block th:case="false">
<th:block th:if="${au.lastActive == null}">
<p th:text="'User has not logged in yet'"></p>
</th:block>
<th:block th:if="${au.lastActive != null}">
<p th:text="'Last active ' + ${au.lastActive}">Last active 3 hours ago</p>
</th:block>
<!-- <p th:case="${au.online == true}" th:text="${au.userName} + ' is online'">Khalid is online</p>
<p th:if="${au.online == false}" th:text="${au.userName} + ' is offline' + ' Last active = ' + ${au.lastActive}">Khalid is offline. -->
<!-- <span th:text="'Last active = ' + ${au.lastActive}"></span> -->
</th:block>
</div>
</div>
<div class="d-flex flex-column ml-auto">
<div class="text-right">Dec 25</div>
<div style="color: rgba(255,255,255,0.7);">Hello how are you</div>
</div>
</div>
</li>
</th:block>
</ui>
</div>
<div class="card-footer"></div>
</div>
</div>
<div class="col-md-8 col-xl-6 chat">
<div class="card" id="no-user-selected">
<div class="m-auto">
<div class="d-flex justify-content-center">
<div class="align-self-center">
<h2 class="display-4 no-user-selected-h2">Please select a user</h2>
<input class="form-control type_msg" size="10" type="password" id="passphrase-initial" placeholder="Passphrase " required>
</div>
</div>
</div>
</div>
<div class="card" id="chat-card" hidden>
<div class="card-header msg_head">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span class="online_icon"></span>
</div>
<div class="user_info">
<span id="user-name-span">Chat with Khalid</span>
<p id="num-messages-p">1767 Messages</p>
</div>
<div class="video_cam">
<span><i class="fas fa-video"></i></span>
<span><i class="fas fa-phone"></i></span>
</div>
</div>
<span id="action_menu_btn"><i class="fas fa-ellipsis-v"></i></span>
<div class="action_menu">
<ul>
<li><i class="fas fa-user-circle"></i> View profile</li>
<li><i class="fas fa-users"></i> Add to close friends</li>
<li><i class="fas fa-plus"></i> Add to group</li>
<li><i class="fas fa-ban"></i> Block</li>
</ul>
</div>
</div>
<div class="card-body msg_card_body" id="chat_area_new">
</div>
<div class="card-footer">
<form action="#" th:object="${chatMessageDTO}" method="post" id="chatMessageForm" class="needs-validation" novalidate>
<div class="input-group">
<div class="input-group-append">
<span class="input-group-text attach_btn"><i class="fas fa-paperclip"></i></span>
</div>
<input class="form-control type_msg" size="10" type="password" id="passphrase" placeholder="Passphrase " required>
<textarea name="" id="chatInput" class="form-control type_msg" placeholder="Type your message..." required></textarea>
<div class="input-group-append">
<button class="input-group-text send_btn"><i class="fas fa-location-arrow"></i></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<section hidden>
<div class="container">
<div class="row">
<div class="col-sm">
<h4 class="display-4 text-center py-2">Chat</h4>
<div class="card text-white bg-primary mb-3 card-form rounded mx-auto">
<div class="card-body rounded">
<!-- <h4 class="card-title">Chat</h4> -->
<div class="form-group">
<textarea id="chatTextArea" class="form-control-lg py-2" disabled></textarea>
</div>
<!-- <form action="#" th:action="@{/seedstartermng}" th:object="${seedStarter}" method="post"> -->
<!-- th:action="@{/api/chat}" -->
<div class="card-text">
</div>
<form action="#" th:object="${chatMessageDTO}" method="post" class="needs-validation" novalidate>
<div class="row">
<div class="col-3">
<div class="form-group">
<label class="lead" for="toUser">User to send to: </label>
<th:block th:each="userName: ${userNames}">
<input class="form-control" type="radio" th:field="*{toUser}" th:value="${userName}">
<label class="btn btn-secondary" th:for="${#ids.prev('toUser')}" th:text="${userName}">
Demo User
</label>
</th:block>
</div>
</div>
<div class="col">
<div class="my-form-inputs container">
<div class="form-group">
<label for="chatInput">Your message: </label>
<textarea class="form-control" type="text" required></textarea>
<div class="invalid-feedback">
Cannot be empty
</div>
</div>
<div class="form-group">
<label for="passphrase">Passphrase: </label>
<input class="form-control" type="password" required>
</div>
<div class="form-group text-center">
<button class="btn btn-secondary mx-auto">Submit</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<template id="msg_container_template">
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg">
</div>
<div class="msg_container">
{{{message}}}
<span class="msg_time">{{time}}</span>
</div>
</div>
</template>
<template id="msg_container_send_template">
<div class="d-flex justify-content-end mb-4">
<div class="msg_container_send">
{{{message}}}
<span class="msg_time_send">{{time}}</span>
</div>
<!-- <div class="img_cont_msg">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg"
class="rounded-circle user_img_msg">
</div> -->
{{{avatar}}}
</div>
</template>
<template id="user-contact-online-template">
<li name="user-box" class="user-box">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
<span class="online_icon"></span>
</div>
<div class="user_info">
<span class="to-user-span">{{userName}}</span>
<p>{{userName}} is online</p>
</div>
<div class="d-flex flex-column ml-auto">
<div class="text-right">Dec 25</div>
<div style="color: rgba(255,255,255,0.7);">Hello how are you</div>
</div>
</div>
</li>
</template>
<template id="user-contact-offline-template">
<li name="user-box" class="user-box">
<div class="d-flex bd-highlight">
<div class="img_cont">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
</div>
<div class="user_info">
<span class="to-user-span">{{userName}}</span>
<p>Last active {{lastActive}}</p>
</div>
<div class="d-flex flex-column ml-auto">
<div class="text-right">Dec 25</div>
<div style="color: rgba(255,255,255,0.7);">Hello how are you</div>
</div>
</div>
</li>
</template>
</body>
<!-- <script th:src="@{js/chat.js}" type="text/javascript"></script> -->
<!-- <script src="../static/js/bundle.js" th:src="@{/js/bundle.js}"></script> -->
<script src="../static/js/chatStatic.js" th:if="false"></script>
</html>
<!-- <div th:include="::frag (${value1},${value2})">...</div>
<div th:include="::frag (onevar=${value1},twovar=${value2})">...</div> -->