From 5e7739a8204f500240191a2c11a4f2eb3b006d4c Mon Sep 17 00:00:00 2001 From: CJACOBSON32 <34342644+CJACOBSON32@users.noreply.github.com> Date: Thu, 1 Sep 2022 13:17:53 -0400 Subject: [PATCH 01/12] Added boilerplate .gitignore and ran npm install --- .gitignore | 49 +++++++++++++++++++++++++++++++++++ .idea/.gitignore | 5 ++++ .idea/cs4241_assignment_2.iml | 12 +++++++++ .idea/modules.xml | 8 ++++++ .idea/vcs.xml | 6 +++++ package-lock.json | 30 +++++++++++++++++++++ 6 files changed, 110 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/cs4241_assignment_2.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cb80ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +node_modules/ + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/cs4241_assignment_2.iml b/.idea/cs4241_assignment_2.iml new file mode 100644 index 0000000..0c8867d --- /dev/null +++ b/.idea/cs4241_assignment_2.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..eb9b380 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..485bd31 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "cs4241_assignment_2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "mime": "^2.4.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + } + }, + "dependencies": { + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" + } + } +} From 722148872f6a3d6c49ec8077d3c977c3600cb572 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Sun, 4 Sep 2022 14:51:30 -0400 Subject: [PATCH 02/12] Added some npm dependencies, switched to typescript, and reformatted code --- .idea/compiler.xml | 6 ++ .idea/jsLibraryMappings.xml | 6 ++ package-lock.json | 66 ++++++++++++++++++- package.json | 7 +- public/index.html | 64 +++++++++--------- public/js/scripts.js | 3 +- public/js/scripts.ts | 3 + server.improved.js | 127 ++++++++++++++++-------------------- server.improved.ts | 67 +++++++++++++++++++ 9 files changed, 241 insertions(+), 108 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/jsLibraryMappings.xml create mode 100644 public/js/scripts.ts create mode 100644 server.improved.ts diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..1a2fb33 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d23208f --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 485bd31..4fcb912 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,35 @@ "packages": { "": { "dependencies": { - "mime": "^2.4.4" + "mime": "^2.4.4", + "typescript": "^4.8.2" + }, + "devDependencies": { + "@types/filesystem": "^0.0.32", + "@types/node": "^18.7.14" + } + }, + "node_modules/@types/filesystem": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz", + "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==", + "dev": true, + "dependencies": { + "@types/filewriter": "*" } }, + "node_modules/@types/filewriter": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz", + "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", + "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", + "dev": true + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -18,13 +44,51 @@ "engines": { "node": ">=4.0.0" } + }, + "node_modules/typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } } }, "dependencies": { + "@types/filesystem": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz", + "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==", + "dev": true, + "requires": { + "@types/filewriter": "*" + } + }, + "@types/filewriter": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz", + "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", + "dev": true + }, + "@types/node": { + "version": "18.7.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", + "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", + "dev": true + }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" + }, + "typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" } } } diff --git a/package.json b/package.json index 988f135..e439954 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,14 @@ "description": "", "author": "", "scripts": { - "start": "node server.improved.js" + "start": "node server.improved.ts" }, "dependencies": { "mime": "^2.4.4" + }, + "devDependencies": { + "@types/filesystem": "^0.0.32", + "@types/node": "^18.7.14", + "typescript": "^4.8.2" } } diff --git a/public/index.html b/public/index.html index c56d620..a488146 100644 --- a/public/index.html +++ b/public/index.html @@ -1,41 +1,41 @@ - - CS4241 Assignment 2 - - - -
- - -
- - + diff --git a/public/js/scripts.js b/public/js/scripts.js index de052ea..7282d9f 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,2 @@ // Add some Javascript code here, to run on the front end. - -console.log("Welcome to assignment 2!") \ No newline at end of file +console.log("Welcome to assignment 2!"); diff --git a/public/js/scripts.ts b/public/js/scripts.ts new file mode 100644 index 0000000..de052ea --- /dev/null +++ b/public/js/scripts.ts @@ -0,0 +1,3 @@ +// Add some Javascript code here, to run on the front end. + +console.log("Welcome to assignment 2!") \ No newline at end of file diff --git a/server.improved.js b/server.improved.js index 26673fc..25609e3 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,72 +1,55 @@ -const http = require( 'http' ), - fs = require( 'fs' ), - // IMPORTANT: you must run `npm install` in the directory for this assignment - // to install the mime library used in the following line of code - mime = require( 'mime' ), - dir = 'public/', - port = 3000 - -const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} -] - -const server = http.createServer( function( request,response ) { - if( request.method === 'GET' ) { - handleGet( request, response ) - }else if( request.method === 'POST' ){ - handlePost( request, response ) - } -}) - -const handleGet = function( request, response ) { - const filename = dir + request.url.slice( 1 ) - - if( request.url === '/' ) { - sendFile( response, 'public/index.html' ) - }else{ - sendFile( response, filename ) - } -} - -const handlePost = function( request, response ) { - let dataString = '' - - request.on( 'data', function( data ) { - dataString += data - }) - - request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) - - // ... do something with the data here!!! - - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() - }) -} - -const sendFile = function( response, filename ) { - const type = mime.getType( filename ) - - fs.readFile( filename, function( err, content ) { - - // if the error = null, then we've loaded the file successfully - if( err === null ) { - - // status code: https://httpstatuses.com - response.writeHeader( 200, { 'Content-Type': type }) - response.end( content ) - - }else{ - - // file not found, error code 404 - response.writeHeader( 404 ) - response.end( '404 Error: File Not Found' ) - - } - }) -} - -server.listen( process.env.PORT || port ) +var http = require('http'), fs = require('fs'), +// IMPORTANT: you must run `npm install` in the directory for this assignment +// to install the mime library used in the following line of code +mime = require('mime'), dir = 'public/', port = 3000; +var appdata = [ + { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, + { 'model': 'honda', 'year': 2004, 'mpg': 30 }, + { 'model': 'ford', 'year': 1987, 'mpg': 14 } +]; +var server = http.createServer(function (request, response) { + if (request.method === 'GET') { + handleGet(request, response); + } + else if (request.method === 'POST') { + handlePost(request, response); + } +}); +var handleGet = function (request, response) { + var filename = dir + request.url.slice(1); + if (request.url === '/') { + sendFile(response, 'public/index.html'); + } + else { + sendFile(response, filename); + } +}; +var handlePost = function (request, response) { + var dataString = ''; + request.on('data', function (data) { + dataString += data; + }); + request.on('end', function () { + console.log(JSON.parse(dataString)); + // ... do something with the data here!!! + response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); + response.end(); + }); +}; +var sendFile = function (response, filename) { + var type = mime.getType(filename); + fs.readFile(filename, function (err, content) { + // if the error = null, then we've loaded the file successfully + if (err === null) { + // status code: https://httpstatuses.com + response.writeHeader(200, { 'Content-Type': type }); + response.end(content); + } + else { + // file not found, error code 404 + response.writeHeader(404); + response.end('404 Error: File Not Found'); + } + }); +}; +server.listen(process.env.PORT || port); diff --git a/server.improved.ts b/server.improved.ts new file mode 100644 index 0000000..1830bdc --- /dev/null +++ b/server.improved.ts @@ -0,0 +1,67 @@ +const http = require( 'http' ), + fs = require( 'fs' ), + // IMPORTANT: you must run `npm install` in the directory for this assignment + // to install the mime library used in the following line of code + mime = require( 'mime' ), + dir = 'public/', + port = 3000; + +const appdata = [ + { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, + { 'model': 'honda', 'year': 2004, 'mpg': 30 }, + { 'model': 'ford', 'year': 1987, 'mpg': 14} +]; + +const server = http.createServer( function( request,response ) { + if( request.method === 'GET' ) { + handleGet( request, response ); + } else if( request.method === 'POST' ){ + handlePost(request, response); + } +}); + +const handleGet = function( request, response ) { + const filename = dir + request.url.slice( 1 ); + + if( request.url === '/' ) { + sendFile( response, 'public/index.html' ); + }else{ + sendFile( response, filename ); + } +} + +const handlePost = function( request, response ) { + let dataString = ''; + + request.on( 'data', function( data ) { + dataString += data; + }); + + request.on( 'end', function() { + console.log( JSON.parse( dataString ) ); + + // ... do something with the data here!!! + + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.end() + }); +} + +const sendFile = function( response, filename ) { + const type = mime.getType( filename ) + + fs.readFile( filename, function( err, content ) { + // if the error = null, then we've loaded the file successfully + if( err === null ) { + // status code: https://httpstatuses.com + response.writeHeader( 200, { 'Content-Type': type }); + response.end( content ); + }else{ + // file not found, error code 404 + response.writeHeader( 404 ); + response.end( '404 Error: File Not Found' ); + } + }) +} + +server.listen( process.env.PORT || port ) From cb13847808981e1d14eaf267725d51370a0c4963 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Wed, 7 Sep 2022 20:46:23 -0400 Subject: [PATCH 03/12] Set up Typescript --- package.json | 2 +- public/js/scripts.js | 1 + server.improved.js | 26 ++++++----- server.improved.ts | 26 +++++++---- tsconfig.json | 103 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 tsconfig.json diff --git a/package.json b/package.json index e439954..9adaba8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "author": "", "scripts": { - "start": "node server.improved.ts" + "start": "node server.improved.js" }, "dependencies": { "mime": "^2.4.4" diff --git a/public/js/scripts.js b/public/js/scripts.js index 7282d9f..35e7a9c 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,2 +1,3 @@ +"use strict"; // Add some Javascript code here, to run on the front end. console.log("Welcome to assignment 2!"); diff --git a/server.improved.js b/server.improved.js index 25609e3..1ea669c 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,13 +1,15 @@ -var http = require('http'), fs = require('fs'), +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const http = require('http'), fs = require('fs'), // IMPORTANT: you must run `npm install` in the directory for this assignment // to install the mime library used in the following line of code mime = require('mime'), dir = 'public/', port = 3000; -var appdata = [ +const appdata = [ { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, { 'model': 'honda', 'year': 2004, 'mpg': 30 }, { 'model': 'ford', 'year': 1987, 'mpg': 14 } ]; -var server = http.createServer(function (request, response) { +const server = http.createServer(function (request, response) { if (request.method === 'GET') { handleGet(request, response); } @@ -15,8 +17,10 @@ var server = http.createServer(function (request, response) { handlePost(request, response); } }); -var handleGet = function (request, response) { - var filename = dir + request.url.slice(1); +const handleGet = function (request, response) { + if (request === undefined) + throw new TypeError("request cannot be undefined"); + const filename = dir + request.url?.slice(1); if (request.url === '/') { sendFile(response, 'public/index.html'); } @@ -24,8 +28,8 @@ var handleGet = function (request, response) { sendFile(response, filename); } }; -var handlePost = function (request, response) { - var dataString = ''; +const handlePost = function (request, response) { + let dataString = ''; request.on('data', function (data) { dataString += data; }); @@ -36,18 +40,18 @@ var handlePost = function (request, response) { response.end(); }); }; -var sendFile = function (response, filename) { - var type = mime.getType(filename); +const sendFile = function (response, filename) { + const type = mime.getType(filename); fs.readFile(filename, function (err, content) { // if the error = null, then we've loaded the file successfully if (err === null) { // status code: https://httpstatuses.com - response.writeHeader(200, { 'Content-Type': type }); + response.writeHead(200, { 'Content-Type': type }); response.end(content); } else { // file not found, error code 404 - response.writeHeader(404); + response.writeHead(404); response.end('404 Error: File Not Found'); } }); diff --git a/server.improved.ts b/server.improved.ts index 1830bdc..35bad38 100644 --- a/server.improved.ts +++ b/server.improved.ts @@ -1,3 +1,8 @@ +import {IncomingMessage, ServerResponse} from "http"; +import {constants} from "os"; +import ErrnoException = NodeJS.ErrnoException; +import {Serializer} from "v8"; + const http = require( 'http' ), fs = require( 'fs' ), // IMPORTANT: you must run `npm install` in the directory for this assignment @@ -12,7 +17,7 @@ const appdata = [ { 'model': 'ford', 'year': 1987, 'mpg': 14} ]; -const server = http.createServer( function( request,response ) { +const server = http.createServer( function( request: IncomingMessage, response: ServerResponse ) { if( request.method === 'GET' ) { handleGet( request, response ); } else if( request.method === 'POST' ){ @@ -20,8 +25,11 @@ const server = http.createServer( function( request,response ) { } }); -const handleGet = function( request, response ) { - const filename = dir + request.url.slice( 1 ); +const handleGet = function( request: IncomingMessage, response: ServerResponse ) { + if (request === undefined) + throw new TypeError("request cannot be undefined"); + + const filename = dir + request.url?.slice( 1 ); if( request.url === '/' ) { sendFile( response, 'public/index.html' ); @@ -30,7 +38,7 @@ const handleGet = function( request, response ) { } } -const handlePost = function( request, response ) { +const handlePost = function( request: IncomingMessage, response: ServerResponse ) { let dataString = ''; request.on( 'data', function( data ) { @@ -47,18 +55,18 @@ const handlePost = function( request, response ) { }); } -const sendFile = function( response, filename ) { +const sendFile = function( response: ServerResponse, filename: string ) { const type = mime.getType( filename ) - fs.readFile( filename, function( err, content ) { + fs.readFile( filename, function( err: ErrnoException, content: Buffer ) { // if the error = null, then we've loaded the file successfully if( err === null ) { // status code: https://httpstatuses.com - response.writeHeader( 200, { 'Content-Type': type }); + response.writeHead( 200, { 'Content-Type': type }); response.end( content ); - }else{ + } else { // file not found, error code 404 - response.writeHeader( 404 ); + response.writeHead( 404 ); response.end( '404 Error: File Not Found' ); } }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..57ed895 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} From 484a7d39a5379aac1fa76ed9ea9eec9835476646 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Wed, 7 Sep 2022 21:53:18 -0400 Subject: [PATCH 04/12] Basic server response --- public/index.html | 16 +++++++--------- server.improved.js | 7 +++++-- server.improved.ts | 9 ++++++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/public/index.html b/public/index.html index a488146..e11a749 100644 --- a/public/index.html +++ b/public/index.html @@ -14,27 +14,25 @@ const submit = function (e) { // prevent default form action from being carried out - e.preventDefault() + e.preventDefault(); const input = document.querySelector('#yourname'), json = {yourname: input.value}, - body = JSON.stringify(json) + body = JSON.stringify(json); fetch('/submit', { method: 'POST', body }) - .then(function (response) { - // do something with the reponse - console.log(response) - }) + .then((response) => response.json()) + .then((json) => console.log(json)); - return false + return false; } window.onload = function () { - const button = document.querySelector('button') - button.onclick = submit + const button = document.querySelector('button'); + button.onclick = submit; } diff --git a/server.improved.js b/server.improved.js index 1ea669c..355f5ef 100644 --- a/server.improved.js +++ b/server.improved.js @@ -34,10 +34,13 @@ const handlePost = function (request, response) { dataString += data; }); request.on('end', function () { - console.log(JSON.parse(dataString)); + let data = { yourname: "" }; + data = JSON.parse(dataString); + console.log(data); // ... do something with the data here!!! + // response.write(dataString); response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); - response.end(); + response.end(dataString); }); }; const sendFile = function (response, filename) { diff --git a/server.improved.ts b/server.improved.ts index 35bad38..3292d39 100644 --- a/server.improved.ts +++ b/server.improved.ts @@ -46,12 +46,15 @@ const handlePost = function( request: IncomingMessage, response: ServerResponse }); request.on( 'end', function() { - console.log( JSON.parse( dataString ) ); + let data = {yourname: ""} + data = JSON.parse(dataString); + console.log(data); // ... do something with the data here!!! + // response.write(dataString); - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(dataString); }); } From b6b1bdb42bfc6d02d7f82d915014c9cd5241dc7d Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 01:41:08 -0400 Subject: [PATCH 05/12] Stuck on GET request --- package.json | 2 +- public/css/style.css | 1 - public/css/styles.css | 95 +++++++++++++++++++ public/index.html | 54 +++++------ public/js/scripts.js | 34 ++++++- public/js/scripts.ts | 50 +++++++++- .../server.improved.js | 19 ++-- .../server.improved.ts | 29 ++++-- 8 files changed, 233 insertions(+), 51 deletions(-) delete mode 100644 public/css/style.css create mode 100644 public/css/styles.css rename server.improved.js => server/server.improved.js (78%) rename server.improved.ts => server/server.improved.ts (76%) diff --git a/package.json b/package.json index 9adaba8..6629e79 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "author": "", "scripts": { - "start": "node server.improved.js" + "start": "node server/server.improved.js" }, "dependencies": { "mime": "^2.4.4" diff --git a/public/css/style.css b/public/css/style.css deleted file mode 100644 index d5f842a..0000000 --- a/public/css/style.css +++ /dev/null @@ -1 +0,0 @@ -/*Style your own assignment! This is fun! */ \ No newline at end of file diff --git a/public/css/styles.css b/public/css/styles.css new file mode 100644 index 0000000..43c8908 --- /dev/null +++ b/public/css/styles.css @@ -0,0 +1,95 @@ +/*fonts*/ +@import +url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); + +* { + font-family: 'Roboto', sans-serif; +} + +/*Global fonts*/ +h1 { + font-size: 60pt; +} + +h1, h2, h3 { + font-weight: 100; +} + +/*Body*/ +body { + margin: 0; + background-color: #ABCADE; +} + +/*Animate a fade-in entry for every site element*/ +:not(body, div, section, html) { + animation: loadin 1s ease forwards; +} + +@keyframes loadin { + from { + opacity: 0; + transform: translateY(10pt); + } + to { + opacity: 1; + transform: translateY(0pt); + } +} + +/*Section Styling*/ +section { + display: flex; + flex-direction: column; + align-items: center; +} + +section:nth-child(odd) { + background-color: #A0BED1; + padding: 20pt 0pt 20pt 0pt; +} + +section:nth-child(even) { + background-color: #ABCADE; + padding: 20pt 0pt 20pt 0pt; +} + +/*Organizational*/ +.content { + width: clamp(400px, 67%, 1280px); +} + +.hbox { + display: flex; + flex-direction: row; +} + +.vbox { + display: flex; + flex-direction: column; +} + +.flex-center { + display: flex; + align-content: center; + justify-items: center; +} + +hr { + margin: 40pt auto; +} + +/*Form styles*/ +#new-note-form > * { + margin: 0 5px; +} + +#message { + flex-grow: 1; +} + +#submit { + width: auto; + padding: 2px 20px; + margin-left: 10%; +} diff --git a/public/index.html b/public/index.html index e11a749..17891ff 100644 --- a/public/index.html +++ b/public/index.html @@ -3,37 +3,31 @@ CS4241 Assignment 2 + + -
- - -
- - + +
+
+
+ +
+ + +
+ +
+
+
+ + diff --git a/public/js/scripts.js b/public/js/scripts.js index 35e7a9c..ce33514 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,33 @@ "use strict"; -// Add some Javascript code here, to run on the front end. -console.log("Welcome to assignment 2!"); +console.log('test'); +// Get prior messages upon page load +async function loadMessages() { + await fetch('/messages') + .then(response => response.json()) + .then(json => { + console.log(json); + const newElement = document.createElement('p'); + newElement.textContent = JSON.stringify(json); + document.getElementById("messages")?.appendChild(newElement); + }); +} +loadMessages(); +const submit = function (e) { + // prevent default form action from being carried out + e.preventDefault(); + const message = document.querySelector('#message'), color = document.getElementById('color'), json = { message: message.value, color: color.value, timeCreated: new Date() }, body = JSON.stringify(json); + fetch('/submit', { + method: 'POST', + body + }) + .then((response) => response.json()) + .then((json) => { + console.log(json); + const newElement = document.createElement('p'); + newElement.textContent = JSON.stringify(json); + document.getElementById("messages")?.appendChild(newElement); + }); + return false; +}; +const button = document.querySelector('#submit'); +button.onclick = submit; diff --git a/public/js/scripts.ts b/public/js/scripts.ts index de052ea..ae7f860 100644 --- a/public/js/scripts.ts +++ b/public/js/scripts.ts @@ -1,3 +1,49 @@ -// Add some Javascript code here, to run on the front end. -console.log("Welcome to assignment 2!") \ No newline at end of file +// Establish types for the message board +type HEX = `#${string}`; +interface Message { + timeCreated: Date; + color: HEX; + message: string; +} +console.log('test'); +// Get prior messages upon page load +async function loadMessages() { + await fetch('/messages') + .then(response => response.json()) + .then(json => { + console.log(json); + const newElement = document.createElement('p'); + newElement.textContent = JSON.stringify(json); + document.getElementById("messages")?.appendChild(newElement); + }); +} + +loadMessages(); + +const submit = function (e: Event) { + // prevent default form action from being carried out + e.preventDefault(); + + const message = document.querySelector('#message'), + color = document.getElementById('color'), + json: Message = {message: message.value, color: color.value, timeCreated: new Date()}, + body = JSON.stringify(json); + + fetch('/submit', { + method: 'POST', + body + }) + .then((response) => response.json()) + .then((json) => { + console.log(json); + const newElement = document.createElement('p'); + newElement.textContent = JSON.stringify(json); + document.getElementById("messages")?.appendChild(newElement); + }); + + return false; +} + +const button = document.querySelector('#submit'); +button.onclick = submit; \ No newline at end of file diff --git a/server.improved.js b/server/server.improved.js similarity index 78% rename from server.improved.js rename to server/server.improved.js index 355f5ef..c975d13 100644 --- a/server.improved.js +++ b/server/server.improved.js @@ -4,10 +4,8 @@ const http = require('http'), fs = require('fs'), // IMPORTANT: you must run `npm install` in the directory for this assignment // to install the mime library used in the following line of code mime = require('mime'), dir = 'public/', port = 3000; -const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14 } +const messages = [ + { timeCreated: new Date(), color: "#ffffff", message: "Test" } ]; const server = http.createServer(function (request, response) { if (request.method === 'GET') { @@ -24,6 +22,15 @@ const handleGet = function (request, response) { if (request.url === '/') { sendFile(response, 'public/index.html'); } + else if (request.url === '/messages') { + // If messages are requested, send a .json of all messages on the server + console.log("Sent messages"); + console.log(messages); + request.on('end', function () { + response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); + response.end(JSON.stringify(messages)); + }); + } else { sendFile(response, filename); } @@ -34,11 +41,9 @@ const handlePost = function (request, response) { dataString += data; }); request.on('end', function () { - let data = { yourname: "" }; - data = JSON.parse(dataString); + const data = JSON.parse(dataString); console.log(data); // ... do something with the data here!!! - // response.write(dataString); response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); response.end(dataString); }); diff --git a/server.improved.ts b/server/server.improved.ts similarity index 76% rename from server.improved.ts rename to server/server.improved.ts index 3292d39..8b27f9b 100644 --- a/server.improved.ts +++ b/server/server.improved.ts @@ -11,10 +11,17 @@ const http = require( 'http' ), dir = 'public/', port = 3000; -const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} + +// Establish types for the message board +type HEX = `#${string}`; +interface Message { + timeCreated: Date; + color: HEX; + message: string; +} + +const messages: Message[] = [ + { timeCreated: new Date(), color: "#ffffff", message: "Test" } ]; const server = http.createServer( function( request: IncomingMessage, response: ServerResponse ) { @@ -33,7 +40,15 @@ const handleGet = function( request: IncomingMessage, response: ServerResponse ) if( request.url === '/' ) { sendFile( response, 'public/index.html' ); - }else{ + } else if (request.url === '/messages') { + // If messages are requested, send a .json of all messages on the server + console.log("Sent messages"); + console.log(messages); + request.on( 'end', function() { + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(JSON.stringify(messages)); + }); + } else { sendFile( response, filename ); } } @@ -46,12 +61,10 @@ const handlePost = function( request: IncomingMessage, response: ServerResponse }); request.on( 'end', function() { - let data = {yourname: ""} - data = JSON.parse(dataString); + const data: Message = JSON.parse(dataString); console.log(data); // ... do something with the data here!!! - // response.write(dataString); response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); response.end(dataString); From 7e7a981bbe2c7bc0dcdf59f0f3dc49ab6cf8ce8c Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 11:52:15 -0400 Subject: [PATCH 06/12] Basic chatting functionality implemented --- package-lock.json | 10 ++++--- public/css/styles.css | 29 +++++++++++++++++++- public/index.html | 7 ++--- public/js/scripts.js | 48 +++++++++++++++++++++++---------- public/js/scripts.ts | 56 ++++++++++++++++++++++++++++----------- server/server.improved.js | 14 ++++++---- server/server.improved.ts | 21 +++++++++------ 7 files changed, 134 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4fcb912..7388dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,12 +5,12 @@ "packages": { "": { "dependencies": { - "mime": "^2.4.4", - "typescript": "^4.8.2" + "mime": "^2.4.4" }, "devDependencies": { "@types/filesystem": "^0.0.32", - "@types/node": "^18.7.14" + "@types/node": "^18.7.14", + "typescript": "^4.8.2" } }, "node_modules/@types/filesystem": { @@ -49,6 +49,7 @@ "version": "4.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -88,7 +89,8 @@ "typescript": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true } } } diff --git a/public/css/styles.css b/public/css/styles.css index 43c8908..ac355e7 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -79,12 +79,39 @@ hr { margin: 40pt auto; } +/*Message styles*/ +.message { + padding: 10px 20px; + border-radius: 5px; + margin: 5px 0; + justify-content: space-between; +} + +.delete-button { + border-radius: 50% / 40%; + border-color: transparent; + background-color: red; + margin-left: 20px; +} + +#messages-section { + overflow-y: scroll; + height: 85vh; +} + /*Form styles*/ +#input-section { + position: fixed; + bottom: 0; + left: 0; + width: 100%; +} + #new-note-form > * { margin: 0 5px; } -#message { +#message-input { flex-grow: 1; } diff --git a/public/index.html b/public/index.html index 17891ff..e28efaa 100644 --- a/public/index.html +++ b/public/index.html @@ -4,12 +4,13 @@ CS4241 Assignment 2 - + +
-
+
@@ -19,7 +20,7 @@
- +
diff --git a/public/js/scripts.js b/public/js/scripts.js index ce33514..fc878de 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,21 +1,42 @@ "use strict"; -console.log('test'); +/** + * Renders a message to an HTML element displaying the message, when it was posted, and a delete button + * @param message + */ +function renderMessage(message) { + const rootDiv = document.createElement('div'); + rootDiv.className = 'message hbox'; + rootDiv.style.backgroundColor = message.color; + rootDiv.innerHTML = ` +

${message.message}

+
+

${message.timeCreated}

+ +
+ `; + const deleteButton = rootDiv.getElementsByClassName('delete-button')[0]; + // deleteButton.onclick = { + // + // } + return rootDiv; +} // Get prior messages upon page load -async function loadMessages() { - await fetch('/messages') - .then(response => response.json()) - .then(json => { - console.log(json); - const newElement = document.createElement('p'); - newElement.textContent = JSON.stringify(json); +fetch('/messages') + .then(response => response.json()) + .then(json => { + console.log(json); + for (const message of json) { + const newElement = renderMessage(message); document.getElementById("messages")?.appendChild(newElement); - }); -} -loadMessages(); + } +}); +// Handle sending a new message const submit = function (e) { // prevent default form action from being carried out e.preventDefault(); - const message = document.querySelector('#message'), color = document.getElementById('color'), json = { message: message.value, color: color.value, timeCreated: new Date() }, body = JSON.stringify(json); + const message = document.querySelector('#message-input'), color = document.getElementById('color'), json = { timeCreated: new Date(), color: color.value, message: message.value }, body = JSON.stringify(json); fetch('/submit', { method: 'POST', body @@ -23,8 +44,7 @@ const submit = function (e) { .then((response) => response.json()) .then((json) => { console.log(json); - const newElement = document.createElement('p'); - newElement.textContent = JSON.stringify(json); + const newElement = renderMessage(json); document.getElementById("messages")?.appendChild(newElement); }); return false; diff --git a/public/js/scripts.ts b/public/js/scripts.ts index ae7f860..4d452bb 100644 --- a/public/js/scripts.ts +++ b/public/js/scripts.ts @@ -6,28 +6,53 @@ interface Message { color: HEX; message: string; } -console.log('test'); -// Get prior messages upon page load -async function loadMessages() { - await fetch('/messages') - .then(response => response.json()) - .then(json => { - console.log(json); - const newElement = document.createElement('p'); - newElement.textContent = JSON.stringify(json); - document.getElementById("messages")?.appendChild(newElement); - }); + +/** + * Renders a message to an HTML element displaying the message, when it was posted, and a delete button + * @param message + */ +function renderMessage(message: Message): HTMLDivElement { + const rootDiv = document.createElement('div'); + rootDiv.className = 'message hbox'; + rootDiv.style.backgroundColor = message.color; + rootDiv.innerHTML = ` +

${message.message}

+
+

${message.timeCreated}

+ +
+ `; + + const deleteButton = rootDiv.getElementsByClassName('delete-button')[0]; + // deleteButton.onclick = { + // + // } + + return rootDiv; } -loadMessages(); +// Get prior messages upon page load +fetch('/messages') + .then(response => response.json()) + .then(json => { + console.log(json); + + for (const message of json) { + const newElement = renderMessage(message); + document.getElementById("messages")?.appendChild(newElement); + } + }); +// Handle sending a new message const submit = function (e: Event) { // prevent default form action from being carried out e.preventDefault(); - const message = document.querySelector('#message'), + const message = document.querySelector('#message-input'), color = document.getElementById('color'), - json: Message = {message: message.value, color: color.value, timeCreated: new Date()}, + json: Message = {timeCreated: new Date(), color: color.value, message: message.value}, body = JSON.stringify(json); fetch('/submit', { @@ -37,8 +62,7 @@ const submit = function (e: Event) { .then((response) => response.json()) .then((json) => { console.log(json); - const newElement = document.createElement('p'); - newElement.textContent = JSON.stringify(json); + const newElement = renderMessage(json); document.getElementById("messages")?.appendChild(newElement); }); diff --git a/server/server.improved.js b/server/server.improved.js index c975d13..66d11f0 100644 --- a/server/server.improved.js +++ b/server/server.improved.js @@ -14,6 +14,9 @@ const server = http.createServer(function (request, response) { else if (request.method === 'POST') { handlePost(request, response); } + else if (request.method === 'DELETE') { + handleDelete(request, response); + } }); const handleGet = function (request, response) { if (request === undefined) @@ -24,9 +27,8 @@ const handleGet = function (request, response) { } else if (request.url === '/messages') { // If messages are requested, send a .json of all messages on the server - console.log("Sent messages"); - console.log(messages); - request.on('end', function () { + request.on('data', (data) => console.log(data)) + .on('end', function () { response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); response.end(JSON.stringify(messages)); }); @@ -42,12 +44,14 @@ const handlePost = function (request, response) { }); request.on('end', function () { const data = JSON.parse(dataString); - console.log(data); - // ... do something with the data here!!! + data.timeCreated = new Date(); + messages.push(data); response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); response.end(dataString); }); }; +function handleDelete(request, response) { +} const sendFile = function (response, filename) { const type = mime.getType(filename); fs.readFile(filename, function (err, content) { diff --git a/server/server.improved.ts b/server/server.improved.ts index 8b27f9b..f579bff 100644 --- a/server/server.improved.ts +++ b/server/server.improved.ts @@ -24,11 +24,13 @@ const messages: Message[] = [ { timeCreated: new Date(), color: "#ffffff", message: "Test" } ]; -const server = http.createServer( function( request: IncomingMessage, response: ServerResponse ) { - if( request.method === 'GET' ) { +const server = http.createServer( function (request: IncomingMessage, response: ServerResponse) { + if (request.method === 'GET') { handleGet( request, response ); - } else if( request.method === 'POST' ){ + } else if (request.method === 'POST'){ handlePost(request, response); + } else if (request.method === 'DELETE') { + handleDelete(request, response); } }); @@ -42,9 +44,8 @@ const handleGet = function( request: IncomingMessage, response: ServerResponse ) sendFile( response, 'public/index.html' ); } else if (request.url === '/messages') { // If messages are requested, send a .json of all messages on the server - console.log("Sent messages"); - console.log(messages); - request.on( 'end', function() { + request.on('data', (data) => console.log(data)) + .on( 'end', function() { response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); response.end(JSON.stringify(messages)); }); @@ -62,15 +63,19 @@ const handlePost = function( request: IncomingMessage, response: ServerResponse request.on( 'end', function() { const data: Message = JSON.parse(dataString); - console.log(data); - // ... do something with the data here!!! + data.timeCreated = new Date(); + messages.push(data); response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); response.end(dataString); }); } +function handleDelete(request: IncomingMessage, response: ServerResponse) { + +} + const sendFile = function( response: ServerResponse, filename: string ) { const type = mime.getType( filename ) From 954864be50a25f56105f78f613ca1738e9980922 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 13:04:38 -0400 Subject: [PATCH 07/12] Moved messages over to database --- .gitignore | 3 + package-lock.json | 157 ++++++++++++++++++++++++++++++++++++-- package.json | 4 +- public/js/scripts.js | 15 +++- public/js/scripts.ts | 17 ++++- server/server.improved.js | 29 ++++++- server/server.improved.ts | 33 +++++++- 7 files changed, 236 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 8cb80ba..5696d95 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ fabric.properties # Editor-based Rest Client .idea/httpRequests + +# Database +messages.json diff --git a/package-lock.json b/package-lock.json index 7388dc9..78759ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,9 @@ "packages": { "": { "dependencies": { - "mime": "^2.4.4" + "@types/nedb": "^1.8.12", + "mime": "^2.4.4", + "nedb": "^1.8.0" }, "devDependencies": { "@types/filesystem": "^0.0.32", @@ -28,11 +30,52 @@ "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", "dev": true }, + "node_modules/@types/nedb": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/@types/nedb/-/nedb-1.8.12.tgz", + "integrity": "sha512-ICDoQMORMjOSqfNFXT4ENXfwwCir1BPblXNm0SPH7C4Q10ou+pvVagcFAJ+rrzf3A47tGU4K/KbzKu7wO9j45Q==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.7.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", - "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", - "dev": true + "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==" + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, + "node_modules/binary-search-tree": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", + "integrity": "sha512-CvNVKS6iXagL1uGwLagSXz1hzSMezxOuGnFi5FHGKqaTO3nPPWrAbyALUzK640j+xOTVm7lzD9YP8W1f/gvUdw==", + "dependencies": { + "underscore": "~1.4.4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } }, "node_modules/mime": { "version": "2.6.0", @@ -45,6 +88,34 @@ "node": ">=4.0.0" } }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/nedb": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", + "integrity": "sha512-ip7BJdyb5m+86ZbSb4y10FCCW9g35+U8bDRrZlAfCI6m4dKwEsQ5M52grcDcVK4Vm/vnPlDLywkyo3GliEkb5A==", + "dependencies": { + "async": "0.2.10", + "binary-search-tree": "0.2.5", + "localforage": "^1.3.0", + "mkdirp": "~0.5.1", + "underscore": "~1.4.4" + } + }, "node_modules/typescript": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", @@ -57,6 +128,11 @@ "engines": { "node": ">=4.2.0" } + }, + "node_modules/underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ==" } }, "dependencies": { @@ -75,22 +151,93 @@ "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", "dev": true }, + "@types/nedb": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/@types/nedb/-/nedb-1.8.12.tgz", + "integrity": "sha512-ICDoQMORMjOSqfNFXT4ENXfwwCir1BPblXNm0SPH7C4Q10ou+pvVagcFAJ+rrzf3A47tGU4K/KbzKu7wO9j45Q==", + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "18.7.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", - "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", - "dev": true + "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==" + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, + "binary-search-tree": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", + "integrity": "sha512-CvNVKS6iXagL1uGwLagSXz1hzSMezxOuGnFi5FHGKqaTO3nPPWrAbyALUzK640j+xOTVm7lzD9YP8W1f/gvUdw==", + "requires": { + "underscore": "~1.4.4" + } + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "requires": { + "immediate": "~3.0.5" + } + }, + "localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "requires": { + "lie": "3.1.1" + } }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "nedb": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", + "integrity": "sha512-ip7BJdyb5m+86ZbSb4y10FCCW9g35+U8bDRrZlAfCI6m4dKwEsQ5M52grcDcVK4Vm/vnPlDLywkyo3GliEkb5A==", + "requires": { + "async": "0.2.10", + "binary-search-tree": "0.2.5", + "localforage": "^1.3.0", + "mkdirp": "~0.5.1", + "underscore": "~1.4.4" + } + }, "typescript": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", "dev": true + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ==" } } } diff --git a/package.json b/package.json index 6629e79..8423636 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "start": "node server/server.improved.js" }, "dependencies": { - "mime": "^2.4.4" + "@types/nedb": "^1.8.12", + "mime": "^2.4.4", + "nedb": "^1.8.0" }, "devDependencies": { "@types/filesystem": "^0.0.32", diff --git a/public/js/scripts.js b/public/js/scripts.js index fc878de..0cc1cef 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,4 +1,5 @@ "use strict"; +const messagesElement = document.getElementById("messages"); /** * Renders a message to an HTML element displaying the message, when it was posted, and a delete button * @param message @@ -17,9 +18,15 @@ function renderMessage(message) {
`; const deleteButton = rootDiv.getElementsByClassName('delete-button')[0]; - // deleteButton.onclick = { - // - // } + deleteButton.onclick = (ev) => { + const messageIndex = Array.from(messagesElement.children).indexOf(rootDiv); + const body = JSON.stringify({ index: messageIndex }); + fetch('/remove', { + method: 'DELETE', + body + }) + .then(response => rootDiv.remove()); + }; return rootDiv; } // Get prior messages upon page load @@ -29,7 +36,7 @@ fetch('/messages') console.log(json); for (const message of json) { const newElement = renderMessage(message); - document.getElementById("messages")?.appendChild(newElement); + messagesElement.appendChild(newElement); } }); // Handle sending a new message diff --git a/public/js/scripts.ts b/public/js/scripts.ts index 4d452bb..293fbe2 100644 --- a/public/js/scripts.ts +++ b/public/js/scripts.ts @@ -7,6 +7,8 @@ interface Message { message: string; } +const messagesElement = document.getElementById("messages"); + /** * Renders a message to an HTML element displaying the message, when it was posted, and a delete button * @param message @@ -26,9 +28,16 @@ function renderMessage(message: Message): HTMLDivElement { `; const deleteButton = rootDiv.getElementsByClassName('delete-button')[0]; - // deleteButton.onclick = { - // - // } + deleteButton.onclick = (ev) => { + const messageIndex = Array.from(messagesElement.children).indexOf(rootDiv); + const body = JSON.stringify({index: messageIndex}); + + fetch('/remove', { + method: 'DELETE', + body + }) + .then(response => rootDiv.remove()); + } return rootDiv; } @@ -41,7 +50,7 @@ fetch('/messages') for (const message of json) { const newElement = renderMessage(message); - document.getElementById("messages")?.appendChild(newElement); + messagesElement.appendChild(newElement); } }); diff --git a/server/server.improved.js b/server/server.improved.js index 66d11f0..a209db1 100644 --- a/server/server.improved.js +++ b/server/server.improved.js @@ -1,9 +1,12 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const Datastore = require('nedb'); const http = require('http'), fs = require('fs'), // IMPORTANT: you must run `npm install` in the directory for this assignment // to install the mime library used in the following line of code mime = require('mime'), dir = 'public/', port = 3000; +// Create message database +const messagesDB = new Datastore({ filename: './messages.json', autoload: true }); const messages = [ { timeCreated: new Date(), color: "#ffffff", message: "Test" } ]; @@ -30,7 +33,10 @@ const handleGet = function (request, response) { request.on('data', (data) => console.log(data)) .on('end', function () { response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); - response.end(JSON.stringify(messages)); + // Sort database then send JSON + messagesDB.find({}).sort({ timeCreated: 1 }).exec((err, docs) => { + response.end(JSON.stringify(docs)); + }); }); } else { @@ -44,13 +50,28 @@ const handlePost = function (request, response) { }); request.on('end', function () { const data = JSON.parse(dataString); + // Update timecreated to server time data.timeCreated = new Date(); - messages.push(data); - response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); - response.end(dataString); + messagesDB.insert(data, (err, newDoc) => { + response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); + response.end(dataString); + // Notify + }); }); }; function handleDelete(request, response) { + if (request.url === '/remove') + request.on('data', (dataString) => { + const data = JSON.parse(dataString); + // Remove from database by sorting by date then removing index + messagesDB.find({}).sort({ timeCreated: 1 }).exec((err, docs) => { + messagesDB.remove(docs[data.index]); + }); + }) + .on('end', function () { + response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); + response.end(); + }); } const sendFile = function (response, filename) { const type = mime.getType(filename); diff --git a/server/server.improved.ts b/server/server.improved.ts index f579bff..19185df 100644 --- a/server/server.improved.ts +++ b/server/server.improved.ts @@ -2,6 +2,8 @@ import {IncomingMessage, ServerResponse} from "http"; import {constants} from "os"; import ErrnoException = NodeJS.ErrnoException; import {Serializer} from "v8"; +import {DataStoreOptions} from "nedb"; +const Datastore = require('nedb') const http = require( 'http' ), fs = require( 'fs' ), @@ -20,6 +22,9 @@ interface Message { message: string; } +// Create message database +const messagesDB: Nedb = new Datastore({ filename: './messages.json', autoload: true }); + const messages: Message[] = [ { timeCreated: new Date(), color: "#ffffff", message: "Test" } ]; @@ -47,7 +52,11 @@ const handleGet = function( request: IncomingMessage, response: ServerResponse ) request.on('data', (data) => console.log(data)) .on( 'end', function() { response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); - response.end(JSON.stringify(messages)); + + // Sort database then send JSON + messagesDB.find({}).sort({timeCreated: 1}).exec((err, docs: Message[]) => { + response.end(JSON.stringify(docs)); + }); }); } else { sendFile( response, filename ); @@ -64,16 +73,32 @@ const handlePost = function( request: IncomingMessage, response: ServerResponse request.on( 'end', function() { const data: Message = JSON.parse(dataString); + // Update timecreated to server time data.timeCreated = new Date(); - messages.push(data); - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); - response.end(dataString); + messagesDB.insert(data, (err, newDoc) => { + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(dataString); + + // Notify + }); }); } function handleDelete(request: IncomingMessage, response: ServerResponse) { + if (request.url === '/remove') + request.on('data', (dataString) => { + const data = <{index: number}>JSON.parse(dataString); + // Remove from database by sorting by date then removing index + messagesDB.find({}).sort({timeCreated: 1}).exec((err, docs: Message[]) => { + messagesDB.remove(docs[data.index]) + }); + }) + .on( 'end', function() { + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(); + }); } const sendFile = function( response: ServerResponse, filename: string ) { From 01be48c80cca87fb7f7d97af6596ade4771a0d75 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 14:02:06 -0400 Subject: [PATCH 08/12] Made chat window auto-adjust height based on screen size --- public/css/styles.css | 9 ++++++++- public/index.html | 10 +++++++++- public/js/scripts.js | 10 ++++++++++ public/js/scripts.ts | 18 +++++++++++++++++- server/server.improved.js | 10 +++------- server/server.improved.ts | 19 ++++++------------- 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/public/css/styles.css b/public/css/styles.css index ac355e7..2b59ab6 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -16,9 +16,16 @@ h1, h2, h3 { } /*Body*/ +html, body { + height: 100vh; + overflow: visible; +} + body { margin: 0; background-color: #ABCADE; + display: flex; + flex-direction: column; } /*Animate a fade-in entry for every site element*/ @@ -96,7 +103,7 @@ hr { #messages-section { overflow-y: scroll; - height: 85vh; + height: 65vh; } /*Form styles*/ diff --git a/public/index.html b/public/index.html index e28efaa..274b603 100644 --- a/public/index.html +++ b/public/index.html @@ -9,10 +9,18 @@
+
+
+

Welcome to Message Board!

+

Try positng a message with a color!

+

You can also delete any message on the board!

+

* Refresh to see new messages

+
+
-
+
    diff --git a/public/js/scripts.js b/public/js/scripts.js index 0cc1cef..87f9aac 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -58,3 +58,13 @@ const submit = function (e) { }; const button = document.querySelector('#submit'); button.onclick = submit; +// Dynamically resize chat feed +function setChatHeight() { + const chatSection = document.getElementById('messages-section'), instructionsSection = document.getElementById('instructions'), inputSection = document.getElementById('input-section'); + chatSection.style.height + = `${window.innerHeight - (instructionsSection.offsetHeight + inputSection.offsetHeight) - 50}px`; +} +setChatHeight(); +window.addEventListener('resize', ev => { + setChatHeight(); +}); diff --git a/public/js/scripts.ts b/public/js/scripts.ts index 293fbe2..c8ab4fd 100644 --- a/public/js/scripts.ts +++ b/public/js/scripts.ts @@ -79,4 +79,20 @@ const submit = function (e: Event) { } const button = document.querySelector('#submit'); -button.onclick = submit; \ No newline at end of file +button.onclick = submit; + +// Dynamically resize chat feed +function setChatHeight() { + const chatSection = document.getElementById('messages-section'), + instructionsSection = document.getElementById('instructions'), + inputSection = document.getElementById('input-section'); + + chatSection.style.height + = `${window.innerHeight - (instructionsSection.offsetHeight + inputSection.offsetHeight) - 50}px`; +} + +setChatHeight(); + +window.addEventListener('resize', ev => { + setChatHeight(); +}); diff --git a/server/server.improved.js b/server/server.improved.js index a209db1..1e925bd 100644 --- a/server/server.improved.js +++ b/server/server.improved.js @@ -7,9 +7,6 @@ const http = require('http'), fs = require('fs'), mime = require('mime'), dir = 'public/', port = 3000; // Create message database const messagesDB = new Datastore({ filename: './messages.json', autoload: true }); -const messages = [ - { timeCreated: new Date(), color: "#ffffff", message: "Test" } -]; const server = http.createServer(function (request, response) { if (request.method === 'GET') { handleGet(request, response); @@ -49,13 +46,12 @@ const handlePost = function (request, response) { dataString += data; }); request.on('end', function () { - const data = JSON.parse(dataString); + const newMessage = JSON.parse(dataString); // Update timecreated to server time - data.timeCreated = new Date(); - messagesDB.insert(data, (err, newDoc) => { + newMessage.timeCreated = new Date(); + messagesDB.insert(newMessage, (err, newDoc) => { response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); response.end(dataString); - // Notify }); }); }; diff --git a/server/server.improved.ts b/server/server.improved.ts index 19185df..6e43edc 100644 --- a/server/server.improved.ts +++ b/server/server.improved.ts @@ -2,8 +2,7 @@ import {IncomingMessage, ServerResponse} from "http"; import {constants} from "os"; import ErrnoException = NodeJS.ErrnoException; import {Serializer} from "v8"; -import {DataStoreOptions} from "nedb"; -const Datastore = require('nedb') +const Datastore = require('nedb'); const http = require( 'http' ), fs = require( 'fs' ), @@ -25,10 +24,6 @@ interface Message { // Create message database const messagesDB: Nedb = new Datastore({ filename: './messages.json', autoload: true }); -const messages: Message[] = [ - { timeCreated: new Date(), color: "#ffffff", message: "Test" } -]; - const server = http.createServer( function (request: IncomingMessage, response: ServerResponse) { if (request.method === 'GET') { handleGet( request, response ); @@ -58,7 +53,7 @@ const handleGet = function( request: IncomingMessage, response: ServerResponse ) response.end(JSON.stringify(docs)); }); }); - } else { + } else { sendFile( response, filename ); } } @@ -71,16 +66,14 @@ const handlePost = function( request: IncomingMessage, response: ServerResponse }); request.on( 'end', function() { - const data: Message = JSON.parse(dataString); + const newMessage: Message = JSON.parse(dataString); // Update timecreated to server time - data.timeCreated = new Date(); + newMessage.timeCreated = new Date(); - messagesDB.insert(data, (err, newDoc) => { + messagesDB.insert(newMessage, (err, newDoc) => { response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); response.end(dataString); - - // Notify }); }); } @@ -118,4 +111,4 @@ const sendFile = function( response: ServerResponse, filename: string ) { }) } -server.listen( process.env.PORT || port ) +server.listen( process.env.PORT || port ); From 548043109a1b23f6113fa2e8290d2e4bc05313b0 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 14:30:05 -0400 Subject: [PATCH 09/12] Made submitting the form clear the textbox --- public/js/scripts.js | 2 ++ public/js/scripts.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/public/js/scripts.js b/public/js/scripts.js index 87f9aac..6becbd4 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -53,6 +53,8 @@ const submit = function (e) { console.log(json); const newElement = renderMessage(json); document.getElementById("messages")?.appendChild(newElement); + // Clear text from input + message.value = ""; }); return false; }; diff --git a/public/js/scripts.ts b/public/js/scripts.ts index c8ab4fd..f8e9b29 100644 --- a/public/js/scripts.ts +++ b/public/js/scripts.ts @@ -73,6 +73,9 @@ const submit = function (e: Event) { console.log(json); const newElement = renderMessage(json); document.getElementById("messages")?.appendChild(newElement); + + // Clear text from input + message.value = ""; }); return false; From b8746126b883cfc835d1b58def78cc328b151e45 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 16:15:31 -0400 Subject: [PATCH 10/12] Added README, added server logging, and made client refresh for new messages. --- README.md | 123 ++++++++++---------------------------- public/css/styles.css | 12 +++- public/index.html | 4 +- public/js/scripts.js | 30 ++++++---- public/js/scripts.ts | 33 ++++++---- server/server.improved.js | 4 ++ server/server.improved.ts | 6 +- 7 files changed, 97 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index f229958..cdf394c 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,39 @@ Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js === -Due: September 8th, by 11:59 AM. +## Message Board +_Message Board_ is a web app that lets users post messages with whatever color they choose. They can post messages and +delete any other messages on the server. -This assignment aims to introduce you to creating a prototype two-tiered web application. -Your application will include the use of HTML, CSS, JavaScript, and Node.js functionality, with active communication between the client and the server over the life of a user session. +By Cameron Jacobson -Baseline Requirements ---- - -There is a large range of application areas and possibilities that meet these baseline requirements. -Try to make your application do something useful! A todo list, storing / retrieving high scores for a very simple game... have a little fun with it. - -Your application is required to implement the following functionalities: - -- a `Server` which not only serves files, but also maintains a tabular dataset with 3 or more fields related to your application -- a `Results` functionality which shows the entire dataset residing in the server's memory -- a `Form/Entry` functionality which allows a user to add, modify, or delete (complete at least two) data items residing in the server's memory -- a `Server Logic` which, upon receiving new or modified "incoming" data, includes and uses a function that adds at least one additional derived field to this incoming data before integrating it with the existing dataset -- the `Derived field` for a new row of data must be computed based on fields already existing in the row. -For example, a `todo` dataset with `task`, `priority`, and `creation_date` may generate a new field `deadline` by looking at `creation_date` and `priority` - -Your application is required to demonstrate the use of the following concepts: - -HTML: -- One or more [HTML Forms](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms), with any combination of form tags appropriate for the user input portion of the application -- A results page displaying all data currently available on the server. You will most likely use a `` tag for this, but `
      ` or `
        ` could also work and might be simpler to work with. Alternatively, you can create a single-page app (see Technical Acheivements) but this is not a requirement. -- All pages should [validate](https://validator.w3.org) -- If your app contains multple pages, they should all be accessible from the homepage (index.html) - -CSS: -- CSS styling of the primary visual elements in the application -- Various CSS Selector functionality must be demonstrated: - - Element selectors - - ID selectors - - Class selectors -- CSS positioning and styling of the primary visual elements in the application: - - Use of either a CSS grid or flexbox for layout - - Rules defining fonts for all text used; no default fonts! Be sure to use a web safe font or a font from a web service like [Google Fonts](http://fonts.google.com/) - -- CSS defined in a maintainable, readable form, in external stylesheets - -JavaScript: -- At minimum, a small amount of front-end JavaScript to get / fetch data from the server; a sample is provided in this repository. - -Node.js: -- An HTTP Server that delivers all necessary files and data for the application, and also creates the required `Derived Fields` in your data. -A starting point is provided in this repository. - -Deliverables ---- - -Do the following to complete this assignment and acheive a base grade of 85%: - -1. Fork the starting project code (make sure to fork the 2022 repo!). This repo contains some starter code that may be used or discarded as needed. -2. Implement your project with the above requirements. -3. Test your project to make sure that when someone goes to your main page, it displays correctly. -4. Deploy your project to Glitch, and fill in the appropriate fields in your package.json file. -5. Ensure that your project has the proper naming scheme `a2-yourGithubUsername` so we can find it. -6. Modify the README to the specifications below, and delete all of the instructions originally found in this README. -7. Create and submit a Pull Request to the original repo. Label the pull request as follows: a2-gitusername-firstname-lastname - -Acheivements ---- - -Below are suggested technical and design achievements. You can use these to help boost your grade up to an A and customize the assignment to your personal interests. These are recommended acheivements, but feel free to create/implement your own... just make sure you thoroughly describe what you did in your README and why it was challenging. ALL ACHIEVEMENTS MUST BE DESCRIBED IN YOUR README IN ORDER TO GET CREDIT FOR THEM. - -*Technical* -- (10 points) Create a single-page app that both provides a form for users to submit data and always shows the current state of the server-side data. To put it another way, when the user submits data, the server should respond sending back the updated data (including the derived field calculated on the server) and the client should then update its data display. - -*Design/UX* -- (5 points per person, with a max of 10 points) Test your user interface with other students in the class. Define a specific task for them to complete (ideally something short that takes <10 minutes), and then use the [think-aloud protocol](https://en.wikipedia.org/wiki/Think_aloud_protocol) to obtain feedback on your design (talk-aloud is also find). Important considerations when designing your study: - -1. Make sure you start the study by clearly stating the task that you expect your user to accomplish. -2. You shouldn't provide any verbal instructions on how to use your interface / accomplish the task you give them. Make sure that your interface is clear enough that users can figure it out without any instruction, or provide text instructions from within the interface itself. -3. If users get stuck to the point where they give up, you can then provde instruction so that the study can continue, but make sure to discuss this in your README. You won't lose any points for this... all feedback is good feedback! - -You'll need to use sometype of collaborative software that will enable you both to see the test subject's screen and listen to their voice as they describe their thoughts. After completing each study, briefly (one to two sentences for each question) address the following in your README: - -1. Provide the last name of each student you conduct the evaluation with. -2. What problems did the user have with your design? -3. What comments did they make that surprised you? -4. What would you change about the interface based on their feedback? - -*You do not need to actually make changes based on their feedback*. This acheivement is designed to help gain experience testing user interfaces. If you run two user studies, you should answer two sets of questions. - -Sample Readme (delete the above when you're ready to submit, and modify the below so with your links and descriptions) ---- - -## Your Web Application Title -Include a very brief summary of your project here. Be sure to include the CSS positioning technique you used, and any required instructions to use your application. +Link to site: https://a2-cjacobson.glitch.me/ ## Technical Achievements -- **Tech Achievement 1**: Using a combination of... - -### Design/Evaluation Achievements -- **Design Achievement 1**: +- **Database**: Uses a nedb database to store messages in the server +- **Results**: Uses a `GET` request at the beginning of every page load to get all previous messages +- **Form/entry**: A form is used to submit new messages with a message and color to the database. + - There is a 🗑 button next to each message that sends a `DELETE` request to the server for that message. +- **Server Logic**: The server updates the date of the received data to log exactly when the server wrote the message +to the database. + - When messages are sent to the client, they are automatically sorted in the server by the date they were posted. +- **Derived Field**: The time is updated when the data is written **(does this count?)** +- **Clientside Javascript**: Javascript on the client is used for several things + - Automatically adjust the css height of the messages element to ensure it fits between the top and bottom bar. + - Clears the input text box after a message has been submitted + - Automatically calls `GET` on the server when the webpage is loaded. + - After getting messages from the server, javascript on the site renders the data to message boxes with css styling + - The color of these elements are set dynamically based on the received color value + +## Design/Evaluation Achievements +### HTML +- **HTML Form**: An **HTML Form** is used to accept a user submitted message with a color. +- **Results page**: there is a feed of messages that displays all user messages on the server so far. +- **Validation** Pages validate 👍 -> https://validator.w3.org/nu/?doc=https%3A%2F%2Fa2-cjacobson.glitch.me%2F + +### CSS +- **Element Selectors**: Element selectors are used to fix the height of the page, remove margins, and add entry +animations for most elements. +- **ID Selectors**: ID selectors are used for referencing individual sections in the webpage. +- **Class selectors**: Several classes are used to style messages and organize site content +- **Google Font**: _Roboto_ was taken from Google Fonts and used for all text content on the site. diff --git a/public/css/styles.css b/public/css/styles.css index 2b59ab6..f1b9e9f 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -94,11 +94,21 @@ hr { justify-content: space-between; } +.message > p { + max-width: 70%; + word-wrap: break-word; +} + .delete-button { - border-radius: 50% / 40%; + border-radius: 50%; border-color: transparent; background-color: red; margin-left: 20px; + width: 50px; + height: 50px; + display: flex; + justify-content: center; + align-items: center; } #messages-section { diff --git a/public/index.html b/public/index.html index 274b603..ca429e2 100644 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,7 @@ - CS4241 Assignment 2 + Message Board @@ -27,7 +27,7 @@

        Welcome to Message Board!

        - +
        diff --git a/public/js/scripts.js b/public/js/scripts.js index 6becbd4..b63b436 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -25,20 +25,28 @@ function renderMessage(message) { method: 'DELETE', body }) - .then(response => rootDiv.remove()); + .then(response => { + rootDiv.remove(); + refreshMessages(); + }); }; return rootDiv; } // Get prior messages upon page load -fetch('/messages') - .then(response => response.json()) - .then(json => { - console.log(json); - for (const message of json) { - const newElement = renderMessage(message); - messagesElement.appendChild(newElement); - } -}); +function refreshMessages(entryAnimation = false) { + fetch('/messages') + .then(response => response.json()) + .then(json => { + messagesElement.innerHTML = ''; + for (const message of json) { + const newElement = renderMessage(message); + if (!entryAnimation) + newElement.style.animation = 'none 0'; + messagesElement.appendChild(newElement); + } + }); +} +refreshMessages(true); // Handle sending a new message const submit = function (e) { // prevent default form action from being carried out @@ -55,6 +63,7 @@ const submit = function (e) { document.getElementById("messages")?.appendChild(newElement); // Clear text from input message.value = ""; + refreshMessages(); }); return false; }; @@ -70,3 +79,4 @@ setChatHeight(); window.addEventListener('resize', ev => { setChatHeight(); }); +window.addEventListener('focus', () => refreshMessages()); diff --git a/public/js/scripts.ts b/public/js/scripts.ts index f8e9b29..b30360a 100644 --- a/public/js/scripts.ts +++ b/public/js/scripts.ts @@ -36,23 +36,33 @@ function renderMessage(message: Message): HTMLDivElement { method: 'DELETE', body }) - .then(response => rootDiv.remove()); + .then(response => { + rootDiv.remove(); + refreshMessages(); + }); } return rootDiv; } // Get prior messages upon page load -fetch('/messages') - .then(response => response.json()) - .then(json => { - console.log(json); +function refreshMessages(entryAnimation = false) { + fetch('/messages') + .then(response => response.json()) + .then(json => { + messagesElement.innerHTML = ''; + + for (const message of json) { + const newElement = renderMessage(message); + if (!entryAnimation) + newElement.style.animation = 'none 0' + + messagesElement.appendChild(newElement); + } + }); +} - for (const message of json) { - const newElement = renderMessage(message); - messagesElement.appendChild(newElement); - } - }); +refreshMessages(true); // Handle sending a new message const submit = function (e: Event) { @@ -76,6 +86,7 @@ const submit = function (e: Event) { // Clear text from input message.value = ""; + refreshMessages(); }); return false; @@ -99,3 +110,5 @@ setChatHeight(); window.addEventListener('resize', ev => { setChatHeight(); }); + +window.addEventListener('focus', () => refreshMessages()) diff --git a/server/server.improved.js b/server/server.improved.js index 1e925bd..dd2c365 100644 --- a/server/server.improved.js +++ b/server/server.improved.js @@ -52,6 +52,8 @@ const handlePost = function (request, response) { messagesDB.insert(newMessage, (err, newDoc) => { response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }); response.end(dataString); + console.log(`User ${request.socket.remoteAddress} posted message`); + console.log(newMessage); }); }); }; @@ -62,6 +64,8 @@ function handleDelete(request, response) { // Remove from database by sorting by date then removing index messagesDB.find({}).sort({ timeCreated: 1 }).exec((err, docs) => { messagesDB.remove(docs[data.index]); + console.log(`User ${request.socket.remoteAddress} deleted item`); + console.log(docs[data.index]); }); }) .on('end', function () { diff --git a/server/server.improved.ts b/server/server.improved.ts index 6e43edc..3331330 100644 --- a/server/server.improved.ts +++ b/server/server.improved.ts @@ -74,6 +74,8 @@ const handlePost = function( request: IncomingMessage, response: ServerResponse messagesDB.insert(newMessage, (err, newDoc) => { response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); response.end(dataString); + console.log(`User ${request.socket.remoteAddress} posted message`); + console.log(newMessage); }); }); } @@ -85,7 +87,9 @@ function handleDelete(request: IncomingMessage, response: ServerResponse) { // Remove from database by sorting by date then removing index messagesDB.find({}).sort({timeCreated: 1}).exec((err, docs: Message[]) => { - messagesDB.remove(docs[data.index]) + messagesDB.remove(docs[data.index]); + console.log(`User ${request.socket.remoteAddress} deleted item`); + console.log(docs[data.index]); }); }) .on( 'end', function() { From 1ef3428e1412c907e093f9385dd8b3efd3fff35c Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 17:57:55 -0400 Subject: [PATCH 11/12] Small update to README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cdf394c..7d651a2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ Link to site: https://a2-cjacobson.glitch.me/ - **Server Logic**: The server updates the date of the received data to log exactly when the server wrote the message to the database. - When messages are sent to the client, they are automatically sorted in the server by the date they were posted. -- **Derived Field**: The time is updated when the data is written **(does this count?)** +- **Derived Field**: The time is updated on the server when the data is written. Responses to Queries from the server +are also sorted by time serverside - **Clientside Javascript**: Javascript on the client is used for several things - Automatically adjust the css height of the messages element to ensure it fits between the top and bottom bar. - Clears the input text box after a message has been submitted From e9bd849e3ef965cac35180c6bc3450faf8b3f525 Mon Sep 17 00:00:00 2001 From: chjm6 Date: Thu, 8 Sep 2022 23:02:12 -0400 Subject: [PATCH 12/12] Added evaluations --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 7d651a2..6c731b8 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,14 @@ animations for most elements. - **ID Selectors**: ID selectors are used for referencing individual sections in the webpage. - **Class selectors**: Several classes are used to style messages and organize site content - **Google Font**: _Roboto_ was taken from Google Fonts and used for all text content on the site. + +### Evaluations +- Aaron Waldman + - He stress-tested the apps to figure out if exotic ascii characters could be used + - He wanted more frequent refreshes from the database + - Based on his feedback, I implemented full updates from the database whenever a change was made or whenever the + lost then gained focus +- Michael O'Connor + - Michael tested how long the strings that could be input were + - He found that long single-word messages would push the delete button off-screen + - In response, I enabled wrapping single-word strings to the next line in messages \ No newline at end of file