-
+
Sunil Rajput
We work directly with our designers and suppliers,
@@ -166,11 +163,8 @@
@@ -181,16 +175,10 @@
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/public_html/js/client.js b/public_html/js/client.js
index 05f0757..b9cb761 100644
--- a/public_html/js/client.js
+++ b/public_html/js/client.js
@@ -1,47 +1,122 @@
-// Constants and variables
-//const wsUri = "ws://localhost:32715";
-const wsUri = "wss://chat.glitch-in.space:443/ws/";
-let websocket;
+"use strict";
+/**
+ * Client class that implements the InstantChat protocol using WebSockets.
+ *
+ * Dispatches the following events which can be subscribed/unsubscribed using .on() and .off():
+ * - initialized: Connection to server has been established and initialized, ready to send messages.
+ * - disconnected: Connection has been closed.
+ * - connectionError: Some connection error occurred.
+ * - receivedMessage: Chat message has been received. Data: object {from: 'username', text: 'text'}
+ */
+class Client extends EventDispatcher {
+ constructor(wsUri, chatID, nickname) {
+ super();
-// Initialization
-function init() {
- console.log("Init...");
- openWebSocket();
+ this.wsUri = wsUri;
+ this.chatID = chatID;
+ this.nickname = nickname;
+
+ // Create WebSocket and set internal callbacks
+ console.log("Initialize Client...")
+ this.webSocket = new WebSocket(wsUri);
+ this.webSocket.onopen = this._onSocketOpen.bind(this);
+ this.webSocket.onclose = this._onSocketClose.bind(this);
+ this.webSocket.onerror = this._onSocketError.bind(this);
+ this.webSocket.onmessage = this._onSocketMessage.bind(this);
+ }
+
+ // Internal WebSocket event handlers
+ _onSocketOpen(evt) {
+ console.log("Connected to " + this.wsUri);
+ this.sendInit(this.chatID, this.nickname);
+ }
+
+ _onSocketClose(evt) {
+ console.log("Connection closed (code " + evt.code + ").");
+ this.dispatch("disconnected");
+ }
+
+ _onSocketError(evt) {
+ console.error("Connection error: ", evt);
+ this.dispatch("connectionError");
+ }
+
+ _onSocketMessage(evt) {
+ console.log("Received: " + evt.data);
+ this._parseMessage(evt.data);
+ }
+
+ /**
+ * Sends an arbitrary command as JSON.
+ *
+ * commandObj: The command as an object ('action' specifies type of command).
+ */
+ sendCommand(commandObj) {
+ const commandJson = JSON.stringify(commandObj);
+ console.log("Sending command: " + commandJson);
+ this.webSocket.send(commandJson);
+ }
+
+ /**
+ * Sends the 'init' command which sets up the session. Also sets the chat ID and nickname.
+ *
+ * chatID: The ID of the chat instance.
+ * nickname: The user's nickname.
+ */
+ sendInit(chatID, nickname) {
+ this.sendCommand({
+ action: "init",
+ chat_id: chatID,
+ nickname: nickname,
+ });
+ }
+
+ /**
+ * Sends the 'message' command which sends a chat message to the chat.
+ *
+ * msgText: The text of the chat message.
+ */
+ sendChatMessage(msgText) {
+ this.sendCommand({
+ action: "message",
+ text: msgText,
+ });
+ }
+
+ /**
+ * Parses an incoming JSON message and dispatches specific events.
+ *
+ * msgText: The content of the message as a JSON string.
+ */
+ _parseMessage(msgString) {
+ try {
+ const msg = JSON.parse(msgString);
+
+ switch (msg.type) {
+ // Response to 'init' command (doesn't have much content, I guess)
+ case "init":
+ console.log("Got init response: ", msg);
+ this.dispatch("initialized");
+ break;
+
+ // Incoming chat message
+ case "message":
+ console.log("Received chat message from '" + msg.from + "', text '" + msg.text + "'");
+ this.dispatch("receivedMessage", {
+ from: msg.from,
+ text: msg.text,
+ });
+ break;
+
+ // TODO Topic change, user join/leave, error, ...
+
+ default:
+ console.error("Unknown message type '" + msg.type + "'");
+ }
+ }
+ catch (e) {
+ console.error("Error parsing message JSON: " + e.message);
+ }
+ }
}
-
-// Open WebSocket
-function openWebSocket() {
- websocket = new WebSocket(wsUri);
- websocket.onopen = function(evt) { onOpen(evt) };
- websocket.onclose = function(evt) { onClose(evt) };
- websocket.onmessage = function(evt) { onMessage(evt) };
- websocket.onerror = function(evt) { onError(evt) };
-}
-
-// WebSocket event handlers
-function onOpen(evt) {
- console.log('Connected to ' + wsUri + '.');
-
- let text = "Meow";
- console.log('Sending "' + text + '".');
- websocket.send(text);
-}
-
-function onClose(evt) {
- console.log("Connection closed (code " + evt.code + "): ", evt);
-}
-
-function onMessage(evt) {
- console.log('Received: "' + evt.data + '".');
-}
-
-function onError(evt) {
- console.error('Connection error: ', evt);
-}
-
-
-// Run script after page is loaded
-$(function() {
- init();
-});
diff --git a/public_html/js/events.js b/public_html/js/events.js
new file mode 100644
index 0000000..7662ff7
--- /dev/null
+++ b/public_html/js/events.js
@@ -0,0 +1,42 @@
+"use strict";
+
+/**
+ * Base class to implement an event system in any class. Events can be dispatched and
+ * subscribed/unsubscribed. Events can have multiple registered callbacks.
+ */
+class EventDispatcher {
+ constructor() {
+ this._events = {};
+ }
+
+ /**
+ * Dispatch a named event. Calls all callbacks that are subscribed to this event.
+ */
+ dispatch(eventName, data = null) {
+ if (this._events[eventName]) {
+ this._events[eventName].forEach((callback) => {
+ callback(data);
+ });
+ }
+ }
+
+ /**
+ * Subscribe to an event.
+ */
+ on(eventName, callback) {
+ if (!this._events[eventName]) {
+ this._events[eventName] = [];
+ }
+ this._events[eventName].push(callback);
+ }
+
+ /**
+ * Unsubscribe from an event. Not sure how to unsubscribe anonymous functions though.
+ * If callback is not found, nothing happens.
+ */
+ off(eventName, callback) {
+ if (this._events[eventName]) {
+ this._events[eventName] = this._events[eventName].filter(item => item !== callback);
+ }
+ }
+}
diff --git a/public_html/js/jquery-3.3.1.js b/public_html/js/jquery-3.3.1.js
deleted file mode 100644
index 9b5206b..0000000
--- a/public_html/js/jquery-3.3.1.js
+++ /dev/null
@@ -1,10364 +0,0 @@
-/*!
- * jQuery JavaScript Library v3.3.1
- * https://jquery.com/
- *
- * Includes Sizzle.js
- * https://sizzlejs.com/
- *
- * Copyright JS Foundation and other contributors
- * Released under the MIT license
- * https://jquery.org/license
- *
- * Date: 2018-01-20T17:24Z
- */
-( function( global, factory ) {
-
- "use strict";
-
- if ( typeof module === "object" && typeof module.exports === "object" ) {
-
- // For CommonJS and CommonJS-like environments where a proper `window`
- // is present, execute the factory and get jQuery.
- // For environments that do not have a `window` with a `document`
- // (such as Node.js), expose a factory as module.exports.
- // This accentuates the need for the creation of a real `window`.
- // e.g. var jQuery = require("jquery")(window);
- // See ticket #14549 for more info.
- module.exports = global.document ?
- factory( global, true ) :
- function( w ) {
- if ( !w.document ) {
- throw new Error( "jQuery requires a window with a document" );
- }
- return factory( w );
- };
- } else {
- factory( global );
- }
-
-// Pass this if window is not defined yet
-} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
-
-// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
-// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
-// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
-// enough that all such attempts are guarded in a try block.
-"use strict";
-
-var arr = [];
-
-var document = window.document;
-
-var getProto = Object.getPrototypeOf;
-
-var slice = arr.slice;
-
-var concat = arr.concat;
-
-var push = arr.push;
-
-var indexOf = arr.indexOf;
-
-var class2type = {};
-
-var toString = class2type.toString;
-
-var hasOwn = class2type.hasOwnProperty;
-
-var fnToString = hasOwn.toString;
-
-var ObjectFunctionString = fnToString.call( Object );
-
-var support = {};
-
-var isFunction = function isFunction( obj ) {
-
- // Support: Chrome <=57, Firefox <=52
- // In some browsers, typeof returns "function" for HTML