diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..f767596
Binary files /dev/null and b/.DS_Store differ
diff --git a/icon192.png b/icon192.png
new file mode 100644
index 0000000..cd035a3
Binary files /dev/null and b/icon192.png differ
diff --git a/icon512.png b/icon512.png
new file mode 100644
index 0000000..8f79164
Binary files /dev/null and b/icon512.png differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..7fdbe38
--- /dev/null
+++ b/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ DMD 3440 - Room of Chat
+
+
+
+
+
+
Sending messages as {UserName}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main.css b/main.css
new file mode 100644
index 0000000..0086299
--- /dev/null
+++ b/main.css
@@ -0,0 +1,47 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ background-color: rgb(32, 49, 88);
+ color:#fff;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+#chatBox {
+ width:100%;
+ position:absolute;
+ bottom:70px;
+ top:60px;
+ right:0px;
+ left:0px;
+ background:#eee;
+ border:1px solid #333;
+ color:#000;
+ padding:15px;
+}
+
+#chatInput {
+ position:fixed;
+ left:0px;
+ bottom:0;
+ width:100%;
+ background-color:#ccc;
+ display: flex;
+ justify-content: space-between;
+ height: 70px;
+
+}
+
+#chatInput input {
+ width:100%;
+ text-indent: 15px;
+}
+
+#chatInput button {
+ margin:0px 0px;
+ padding:0px 10px;
+ background-color:#333;
+ color:#fff;
+ border:1px solid #333;
+}
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..0d21f61
--- /dev/null
+++ b/main.js
@@ -0,0 +1,84 @@
+// Register a service worker, this one located in serviceworker.js
+// A service worker is a piece of code the browser runs behind the scenes.
+if ('serviceWorker' in navigator) {
+ console.log('CLIENT: service worker registration in progress.');
+ navigator.serviceWorker.register('sw.js').then(function() {
+ console.log('CLIENT: service worker registration complete.');
+ }, function() {
+ console.log('CLIENT: service worker registration failure.');
+ });
+ } else {
+ console.log('CLIENT: service workers are not supported.');
+ }
+
+ let allMessages = [];
+ let chatBox = document.querySelector("#chatBox");
+ let newMsgInput = document.querySelector("#newMsg");
+ let userName = localStorage.getItem('userName') || "me";
+ let notifSound = 'notification.mp3';
+ let notifSoundElement = new Audio(notifSound);
+ let usernameInput = document.querySelector("#usernameInput");
+ usernameInput.innerHTML = userName;
+
+ function clearChatInput() {
+ document.querySelector("#newMsg").value = "";
+ document.querySelector("#newMsg").focus();
+ }
+
+ function displayNewMessage(msg) {
+ var newMsgP = document.createElement("p");
+ newMsgP.innerHTML = "" + msg.sentBy + ": " + msg.message;
+ chatBox.appendChild(newMsgP);
+
+ window.navigator.vibrate(200);
+
+ notifSoundElement.play();
+ clearChatInput();
+ }
+
+ function makeNewMessageFromLocalUser() {
+ let usrMsg = {
+ sentBy: userName,
+ dateStamp: Date.now(),
+ message: newMsgInput.value
+ }
+
+ if(usrMsg.message !== "") {
+ allMessages.push(usrMsg);
+ // end make message
+ displayNewMessage(usrMsg)
+ }
+ }
+
+ //** All the triggers and event listener assignments down here... */
+
+ // assign the "Make New Message" function to the button click
+ document.querySelector("#sendBtn").addEventListener('click', function(){
+ makeNewMessageFromLocalUser()
+ });
+
+ // since we're not using a real form, assign it to the "keyup return" of the message input too
+
+ document.querySelector("#newMsg").addEventListener('keyup', function(e) {
+ if (e.keyCode === 13) {
+ makeNewMessageFromLocalUser();
+ }
+ });
+
+ // when username input is changed, update localstorage and global variable
+ usernameInput.addEventListener('blur', function() {
+ var oldUsername = localStorage.getItem("userName");
+ var newUsername = this.innerHTML;
+
+ var alertNew = {
+ sentBy: "system",
+ dateStamp: Date.now(),
+ message: `${oldUsername} has changed their name to ${newUsername}`
+ }
+
+ displayNewMessage(alertNew);
+
+
+ localStorage.setItem("userName", newUsername);
+ userName = localStorage.getItem("userName");
+ });
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..c98c45c
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,21 @@
+{
+ "short_name": "Room of Chat",
+ "name": "Room of Chat",
+ "icons": [
+ {
+ "src":"icon192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icon512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": "index.html",
+ "background_color": "#f00",
+ "theme_color": "#f00",
+ "display": "fullscreen"
+ }
+
\ No newline at end of file
diff --git a/notification.mp3 b/notification.mp3
new file mode 100644
index 0000000..d8f9071
Binary files /dev/null and b/notification.mp3 differ
diff --git a/sw.js b/sw.js
new file mode 100644
index 0000000..d2e3d44
--- /dev/null
+++ b/sw.js
@@ -0,0 +1,202 @@
+
+
+console.log('SERVICE WORKER: executing.');
+
+/* A version number is useful when updating the worker logic,
+ allowing you to remove outdated cache entries during the update.
+*/
+var version = 'v1::';
+
+/* These resources will be downloaded and cached by the service worker
+ during the installation process. If any resource fails to be downloaded,
+ then the service worker won't be installed either.
+*/
+var offlineFiles = [
+ '',
+ 'icon192.png',
+ 'icon512.png',
+ 'index.html',
+ 'main.css',
+ 'main.js'
+];
+
+/* The install event fires when the service worker is first installed.
+ You can use this event to prepare the service worker to be able to serve
+ files while visitors are offline.
+*/
+self.addEventListener("install", function(event) {
+ console.log('WORKER: install event in progress.');
+ /* Using event.waitUntil(p) blocks the installation process on the provided
+ promise. If the promise is rejected, the service worker won't be installed.
+ */
+ event.waitUntil(
+ /* The caches built-in is a promise-based API that helps you cache responses,
+ as well as finding and deleting them.
+ */
+ caches
+ /* You can open a cache by name, and this method returns a promise. We use
+ a versioned cache name here so that we can remove old cache entries in
+ one fell swoop later, when phasing out an older service worker.
+ */
+ .open(version + 'fundamentals')
+ .then(function(cache) {
+ /* After the cache is opened, we can fill it with the offline fundamentals.
+ The method below will add all resources in `offlineFiles` to the
+ cache, after making requests for them.
+ */
+ return cache.addAll(offlineFiles);
+ })
+ .then(function() {
+ console.log('WORKER: install completed');
+ })
+ );
+});
+
+/* The fetch event fires whenever a page controlled by this service worker requests
+ a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it
+ comprehends even the request for the HTML page on first load, as well as JS and
+ CSS resources, fonts, any images, etc.
+*/
+self.addEventListener("fetch", function(event) {
+ console.log('WORKER: fetch event in progress.');
+
+ /* We should only cache GET requests, and deal with the rest of method in the
+ client-side, by handling failed POST,PUT,PATCH,etc. requests.
+ */
+ if (event.request.method !== 'GET') {
+ /* If we don't block the event as shown below, then the request will go to
+ the network as usual.
+ */
+ console.log('WORKER: fetch event ignored.', event.request.method, event.request.url);
+ return;
+ }
+ /* Similar to event.waitUntil in that it blocks the fetch event on a promise.
+ Fulfillment result will be used as the response, and rejection will end in a
+ HTTP response indicating failure.
+ */
+ event.respondWith(
+ caches
+ /* This method returns a promise that resolves to a cache entry matching
+ the request. Once the promise is settled, we can then provide a response
+ to the fetch request.
+ */
+ .match(event.request)
+ .then(function(cached) {
+ /* Even if the response is in our cache, we go to the network as well.
+ This pattern is known for producing "eventually fresh" responses,
+ where we return cached responses immediately, and meanwhile pull
+ a network response and store that in the cache.
+
+ Read more:
+ https://ponyfoo.com/articles/progressive-networking-serviceworker
+ */
+ var networked = fetch(event.request)
+ // We handle the network request with success and failure scenarios.
+ .then(fetchedFromNetwork, unableToResolve)
+ // We should catch errors on the fetchedFromNetwork handler as well.
+ .catch(unableToResolve);
+
+ /* We return the cached response immediately if there is one, and fall
+ back to waiting on the network as usual.
+ */
+ console.log('WORKER: fetch event', cached ? '(cached)' : '(network)', event.request.url);
+ return cached || networked;
+
+ function fetchedFromNetwork(response) {
+ /* We copy the response before replying to the network request.
+ This is the response that will be stored on the ServiceWorker cache.
+ */
+ var cacheCopy = response.clone();
+
+ console.log('WORKER: fetch response from network.', event.request.url);
+
+ caches
+ // We open a cache to store the response for this request.
+ .open(version + 'pages')
+ .then(function add(cache) {
+ /* We store the response for this request. It'll later become
+ available to caches.match(event.request) calls, when looking
+ for cached responses.
+ */
+ return cache.put(event.request, cacheCopy);
+ })
+ .then(function() {
+ console.log('WORKER: fetch response stored in cache.', event.request.url);
+ });
+
+ // Return the response so that the promise is settled in fulfillment.
+ return response;
+ }
+
+ /* When this method is called, it means we were unable to produce a response
+ from either the cache or the network. This is our opportunity to produce
+ a meaningful response even when all else fails. It's the last chance, so
+ you probably want to display a "Service Unavailable" view or a generic
+ error response.
+ */
+ function unableToResolve () {
+ /* There's a couple of things we can do here.
+ - Test the Accept header and then return one of the `offlineFiles`
+ e.g: `return caches.match('/some/cached/image.png')`
+ - You should also consider the origin. It's easier to decide what
+ "unavailable" means for requests against your origins than for requests
+ against a third party, such as an ad provider.
+ - Generate a Response programmaticaly, as shown below, and return that.
+ */
+
+ console.log('WORKER: fetch request failed in both cache and network.');
+
+ /* Here we're creating a response programmatically. The first parameter is the
+ response body, and the second one defines the options for the response.
+ */
+ return new Response('
Service Unavailable
', {
+ status: 503,
+ statusText: 'Service Unavailable',
+ headers: new Headers({
+ 'Content-Type': 'text/html'
+ })
+ });
+ }
+ })
+ );
+});
+
+/* The activate event fires after a service worker has been successfully installed.
+ It is most useful when phasing out an older version of a service worker, as at
+ this point you know that the new worker was installed correctly. In this example,
+ we delete old caches that don't match the version in the worker we just finished
+ installing.
+*/
+self.addEventListener("activate", function(event) {
+ /* Just like with the install event, event.waitUntil blocks activate on a promise.
+ Activation will fail unless the promise is fulfilled.
+ */
+ console.log('WORKER: activate event in progress.');
+
+ event.waitUntil(
+ caches
+ /* This method returns a promise which will resolve to an array of available
+ cache keys.
+ */
+ .keys()
+ .then(function (keys) {
+ // We return a promise that settles when all outdated caches are deleted.
+ return Promise.all(
+ keys
+ .filter(function (key) {
+ // Filter by keys that don't start with the latest version prefix.
+ return !key.startsWith(version);
+ })
+ .map(function (key) {
+ /* Return a promise that's fulfilled
+ when each outdated cache is deleted.
+ */
+ return caches.delete(key);
+ })
+ );
+ })
+ .then(function() {
+ console.log('WORKER: activate completed.');
+ })
+ );
+});
\ No newline at end of file