From 699b475d0f4d1e343d9ef6886738dcba92162b93 Mon Sep 17 00:00:00 2001 From: Evan Goldstein Date: Fri, 6 Sep 2019 00:00:20 +0000 Subject: [PATCH 1/7] Change form --- public/index.html | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/public/index.html b/public/index.html index c56d620e..97d86d5c 100755 --- a/public/index.html +++ b/public/index.html @@ -5,10 +5,20 @@ -
- - -
+
+
+ + +
+
+ + +
+
+ + +
+
+ From 22ae6d49473defc3d612762daef1f49712444d31 Mon Sep 17 00:00:00 2001 From: Evan Goldstein Date: Fri, 6 Sep 2019 00:10:24 +0000 Subject: [PATCH 4/7] Actually use the css --- public/index.html | 83 +++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/public/index.html b/public/index.html index 21273d51..d74854fe 100755 --- a/public/index.html +++ b/public/index.html @@ -1,53 +1,52 @@ - + CS4241 Assignment 2 - - -
-
- - -
-
- - -
-
- - -
-
- -
+ + + + +
+ + +
+
+ + +
+
+ + +
- - + window.onload = function() { + const button = document.querySelector( 'button' ) + button.onclick = submit + } + + From 40033077d89480bef90dfbed3c55c04b1ca837b8 Mon Sep 17 00:00:00 2001 From: Evan Goldstein Date: Fri, 6 Sep 2019 00:25:45 +0000 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=92=82=F0=9F=91=A9=E2=80=8D=E2=9D=A4?= =?UTF-8?q?=EF=B8=8F=E2=80=8D=F0=9F=92=8B=E2=80=8D=F0=9F=91=A9=20Checkpoin?= =?UTF-8?q?t=20./public/css/style.css:1014956/247?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index 14395c3d..f55a66e3 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -13,8 +13,8 @@ form div + div { } label { - /* To make sure that all labels have the same size and are properly aligned */ - display: inline-block; + display: flex; + /* add flex options */ width: 90px; text-align: right; } From 06d148ad129ea82bb9ffa35513008d2c9667c63f Mon Sep 17 00:00:00 2001 From: Evan Goldstein Date: Sun, 8 Sep 2019 23:11:30 -0400 Subject: [PATCH 6/7] Database and results code --- .idea/jsLibraryMappings.xml | 6 + .npmrc | 1 + package.json | 3 +- public/index.html | 70 +++++------- public/js/scripts.js | 46 +++++++- public/results.html | 14 +++ public/results.js | 56 ++++++++++ server.improved.js | 211 ++++++++++++++++++++++++------------ 8 files changed, 292 insertions(+), 115 deletions(-) create mode 100644 .idea/jsLibraryMappings.xml create mode 100644 .npmrc create mode 100644 public/results.html create mode 100644 public/results.js diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 00000000..d23208fb --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..5fca0d51 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +scripts-prepend-node-path=true diff --git a/package.json b/package.json index 988f135f..d758ad49 100755 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "start": "node server.improved.js" }, "dependencies": { - "mime": "^2.4.4" + "mime": "^2.4.4", + "sqlite3": "^4.1.0" } } diff --git a/public/index.html b/public/index.html index d74854fe..d839b225 100755 --- a/public/index.html +++ b/public/index.html @@ -1,52 +1,36 @@ - + CS4241 Assignment 2 - - -
-
- - -
-
- - -
-
- - -
+ + + + +
+ + +
+
+ + +
+
+ + +
+
+ +
- - + diff --git a/public/js/scripts.js b/public/js/scripts.js index de052eae..1b1e5a7e 100755 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,47 @@ // 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!"); + +const submit = function (e) { + e.preventDefault(); // prevent default form action from being carried out + + const + name = document.querySelector('#name'), + contentType = getContentType(), + content = document.querySelector('#content'), + json = { + name: name.value, + type: contentType, + content: content.value + }, + body = JSON.stringify(json); + + + fetch('/submit', {method: 'POST', body}) + .then( + function (response) { + // do something with the reponse + console.log(response) + }); + + return false +} + +window.onload = function () { + const button = document.querySelector('#submitButton'); + button.onclick = submit; + updateContentLabel(); +}; + +function getContentType() { + let typeSelector = document.querySelector('#contentTypeSelector'); + return typeSelector.options[typeSelector.selectedIndex].value; +} + +function updateContentLabel() { + document.getElementById('contentLabel').innerHTML = 'Your ' + getContentType() + ": "; +} + + + + diff --git a/public/results.html b/public/results.html new file mode 100644 index 00000000..da21ea62 --- /dev/null +++ b/public/results.html @@ -0,0 +1,14 @@ + + + + + Entertainment! + + + + +
+ + + + diff --git a/public/results.js b/public/results.js new file mode 100644 index 00000000..84c36994 --- /dev/null +++ b/public/results.js @@ -0,0 +1,56 @@ +function generateTableHead(table, data) { + let thead = table.createTHead(); + let row = thead.insertRow(); + for (let key in data[0]) { + if (Object.prototype.hasOwnProperty.call(data[0], key)) { + let th = document.createElement("th"); + let text = document.createTextNode(key); + th.appendChild(text); + row.appendChild(th); + } + } +} + +function fillTable(table, data) { + console.log("Filling with data: ", data); + let tbody = table.createTBody(); + data.forEach(function (item) { + console.log(item); + let row = tbody.insertRow(); + for (let key in item) { + if (Object.prototype.hasOwnProperty.call(item, key)) { + // console.log(key, ": ", item[key]); + let th = document.createElement("th"); + let text = document.createTextNode(item[key]); + th.appendChild(text); + row.appendChild(th); + } + } + }); +} + + +let table = document.querySelector("table"); + +const getUrl = window.location; +const baseUrl = getUrl.protocol + "//" + getUrl.host + "/get-results"; +console.log("url: ", baseUrl); +fetch(baseUrl) + .then(function (data) { + data.text().then(function (data) { + console.log(data); + let json = JSON.parse(data); + generateTableHead(table, json); + fillTable(table, json); + }); + }) + .catch(function (error) { + // If there is any error you will catch them here + }); + +// client.get(baseUrl, function (response) { +// let data = JSON.parse(response); +// console.log(data); +// }); + + diff --git a/server.improved.js b/server.improved.js index 26673fc0..3e32eeb3 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,72 +1,143 @@ -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 sqlite3 = require('sqlite3').verbose(); + +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 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 if (request.url === '/get-results') { + sendResults(response) + }else { + sendFile(response, filename) + } +}; + +const handlePost = function (request, response) { + let dataString = ''; + + request.on('data', function (data) { + dataString += data + }); + + request.on('end', function () { + let data = JSON.parse(dataString); + console.log(data); + + db.run(`INSERT INTO content(contentText, contentType, submittedBy) VALUES('` + + data.content.replace('\'', '"') + `', '` + + data.type.replace('\'', '"') + `', '` + + data.name.replace('\'', '"') + `')`, function (err) { + if (err) { + return console.log(err.message); + } + console.log(`A row has been inserted with rowid ${this.lastID}`); }); + + response.writeHead(200, "OK", {'Content-Type': 'text/plain'}); + response.end() + }) } -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 ) +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') + + } + }) +}; + +const sendResults = function (response) { +// open the database + let db = new sqlite3.Database('./database.db'); + console.log("getting data!"); + let sql = `SELECT * FROM content + ORDER BY contentType`; + let content = []; + db.all(sql, [], (err, rows) => { + if (err) { + throw err; + } + rows.forEach((row) => { + console.log(row); + content.push(row); + }); + response.writeHeader(200, {'Content-Type': 'application/json'}); + response.write(JSON.stringify(rows)); + response.end(); + }); + + db.close(); + // console.log(content); + +}; + + +let db = new sqlite3.Database('./database.db', (err) => { + if (err) + console.error(err.message); + console.log('Connected to the database.'); +}); + +db.run("CREATE TABLE IF NOT EXISTS content(" + + "contentText text PRIMARY KEY, " + + "contentType text NOT NULL, " + + "submittedBy text NOT NULL, " + + "contentLength number DEFAULT 0)", + (err) => { + if (err) + console.error(err.message); + console.log('Connected to the database.'); + }); + + +// db.serialize(() => { +// db.each(`SELECT +// PlaylistId as id, +// Name as name +// FROM playlists`, (err, row) => { +// if (err) { +// console.error(err.message); +// } +// console.log(row.id + "\t" + row.name); +// }); +// }); +// +// db.close((err) => { +// if (err) { +// console.error(err.message); +// } +// console.log('Close the database connection.'); +// }); + + server.listen(process.env.PORT || port); From b9e9e83c475dac4ae5454b2ce706fe7b22ca7b2b Mon Sep 17 00:00:00 2001 From: Evan Goldstein Date: Mon, 9 Sep 2019 11:39:35 -0400 Subject: [PATCH 7/7] CSS and README --- README.md | 104 ++++++++----------------------------------- public/css/style.css | 39 +++++++++++++--- public/index.html | 4 +- public/js/scripts.js | 9 +++- public/results.html | 13 ++++-- public/results.js | 14 +++++- server.improved.js | 20 ++++++--- 7 files changed, 98 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index 2c6e21e2..ad98d2eb 100755 --- a/README.md +++ b/README.md @@ -1,89 +1,21 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js -=== - -Due: September 9th, by 11:59 AM. - -This assignment aims to introduce you to the concepts and practice involved in creating a prototype (i.e. not deployment ready) two-tiered web application. - -The baseline aims of this assignment involve creating an application that demonstrates the use of several specific pieces of HTML, CSS, JavaScript, and Node.js functionality. -Another aim of this assignment is to establish creative boundaries in which you and your partner can explore designing, implementing, and evaluating usable, useful, novel, and technically efficient web applications. - -Baseline Requirements ---- - -Note that there is a very large range of application areas and possibilities that meet these baseline requirements. -Games, internet of things, organizational tools, commerce, media - all are possibilities with a two-tiered form-focused web application. - -Do not limit yourselves to any of the examples given below. -Examples like the upcoming `efficiency_ratio` idea for the `cars` dataset are meant to be illustrative and easy to understand. -They are not intended to be sensible or useful ideas. - -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 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 `cars` dataset with `year`, `horsepower`, and `fuel_efficiency` may create a new field `efficiency_ratio` by dividing `fuel_efficiency` by `horsepower` - -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 - - Clarification: the results page can be implemented in any way. `
`s, `table`s, and `list`s are common choices - -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 sizing of the primary visual elements in the application: - - CSS to cause at least one element to be horizontally centered on the page - - CSS to cause at least one pair of elements to appear side-by-side - - 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. A starting point is provided in this repository. - -Deliverables ---- - -Do the following to complete this assignment: - -1. Fork the starting project code. 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-yourname` so we can find it. -6. Modify the Readme to the specifications below. -7. Create and submit a Pull Request to the original repo. Only one member needs to submit a pull request. - -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. -Images are encouraged, along with concise, high-level text. - -Here is a sample formula for summarizing your activities, talk about: -- the domain area the project pertains to -- the main challenges or problems the application addresses -- the key innovations that make it possible to address the problem -- the main results of the implementation, does it really address the problem? -- any additional implications of the resulting application, or possibly areas for future work that have been discovered as part of the design and implementation activities - -(Note that when I use the above formula, I aim to have only one sentence per thought in order to remain concise.) - -http://a2-charlieroberts.glitch.me +## Entertainment Collector +This application allows users to submit their own jokes and facts for +all to see. The app addresses the problem of not being able to keep track +of all your great jokes and facts. To solve this problem, a SQLite3 database +was used to store the content, and an HTML form was used to submit the content. +To view the submitted content, an HTML table is automatically populated using Javascript. +This implementation solves the problem, but is missing certain niceties such as a sign-in +to keep your content safe from others. Future work on this app would include a sign-in, a better +database system, and the ability for search and sort the content when viewing it. + +http://a2-ebgoldstein.glitch.me ## Technical Achievements -- **Tech Achievement 1**: Using a combination of... -- **Tech Achievement 2**: ... - +- **Tech Achievement 1**: Using Javascript to store information in a SQLite database and recall it +- **Tech Achievement 2**: Using Javascript and Node.js to build a client-server architecture in which the server + is capable of returning results in a JSON format via HTTP and the client can display those results +- **Tech Achievement 3**: Automatically populating a table with Javascript +- **Tech Achievement 4**: Changing HTML text according to a dropdown using javascript ### Design/Evaluation Achievements -- **Design Achievement 1**: Shown in `style.css`, the code... -- **Design Achievement 2**: We tested the application with n=X users, finding that... +- **Design Achievement 1**: Shown in `style.css`, the site is styled to be minimalistic and functional. +- **Design Achievement 2**: Shown in `style.css`, the results table uses alternating row colors for easy reading diff --git a/public/css/style.css b/public/css/style.css index f55a66e3..f936d95e 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,11 +1,23 @@ +body { + background-color: lightskyblue; + display: flex; + flex-direction: column; +} + +body.results { + align-items: flex-start; +} + form { + display: flex; + flex-direction: column; + /* Just to center the form on the page */ margin: 0 auto; - width: 400px; + width: 500px; /* To see the outline of the form */ padding: 1em; - border: 1px solid #CCC; - border-radius: 1em; + border: 2px solid #333333; } form div + div { @@ -22,10 +34,10 @@ label { input, textarea { /* To make sure that all text fields have the same font settings By default, textareas have a monospace font */ - font: 1em sans-serif; - + font: 1em "Franklin Gothic Book"; + background-color: darkorange; /* To give the same size to all text fields */ - width: 300px; + width: 90%; box-sizing: border-box; /* To harmonize the look & feel of text field border */ @@ -55,3 +67,18 @@ button { between the labels and their text fields */ margin-left: .5em; } + +tr:nth-child(even) { + background-color: #f2f2f2 +} + +th { + border: 1px solid gray; + background-color: darkorange; +} + +table { + width: 85%; + background-color: #999999; +} +.col{display:inline-block; border-bottom:1px dashed #333333;width:100px;} diff --git a/public/index.html b/public/index.html index d839b225..b9f3de7d 100755 --- a/public/index.html +++ b/public/index.html @@ -9,7 +9,7 @@
- +
@@ -26,6 +26,8 @@
+ +
diff --git a/public/js/scripts.js b/public/js/scripts.js index 1b1e5a7e..2a3c5d3d 100755 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -25,14 +25,21 @@ const submit = function (e) { }); return false -} +}; window.onload = function () { const button = document.querySelector('#submitButton'); + const resultsButton = document.querySelector('#resultsButton'); button.onclick = submit; + resultsButton.onclick = goToResults; updateContentLabel(); }; +const goToResults = function(){ + const url = window.location; + window.location.href = url.protocol + "//" + url.host + "/results.html"; +}; + function getContentType() { let typeSelector = document.querySelector('#contentTypeSelector'); return typeSelector.options[typeSelector.selectedIndex].value; diff --git a/public/results.html b/public/results.html index da21ea62..5ec62310 100644 --- a/public/results.html +++ b/public/results.html @@ -3,11 +3,18 @@ Entertainment! + + - - + +
+ +
+
+
-
+ +
diff --git a/public/results.js b/public/results.js index 84c36994..5e38c088 100644 --- a/public/results.js +++ b/public/results.js @@ -20,7 +20,7 @@ function fillTable(table, data) { for (let key in item) { if (Object.prototype.hasOwnProperty.call(item, key)) { // console.log(key, ": ", item[key]); - let th = document.createElement("th"); + let th = document.createElement("td"); let text = document.createTextNode(item[key]); th.appendChild(text); row.appendChild(th); @@ -29,6 +29,18 @@ function fillTable(table, data) { }); } +window.onload = function () { + const button = document.querySelector('#submissionButton'); + button.onclick = goToSubmission; + updateContentLabel(); +}; + + +const goToSubmission = function(){ + const url = window.location; + window.location.href = url.protocol + "//" + url.host + "/index.html"; +}; + let table = document.querySelector("table"); diff --git a/server.improved.js b/server.improved.js index 3e32eeb3..218d020f 100644 --- a/server.improved.js +++ b/server.improved.js @@ -30,6 +30,10 @@ const handleGet = function (request, response) { } }; +function validate(str) { + return str.replace('\'', '\'\''); +} + const handlePost = function (request, response) { let dataString = ''; @@ -39,16 +43,18 @@ const handlePost = function (request, response) { request.on('end', function () { let data = JSON.parse(dataString); - console.log(data); - - db.run(`INSERT INTO content(contentText, contentType, submittedBy) VALUES('` + - data.content.replace('\'', '"') + `', '` + - data.type.replace('\'', '"') + `', '` + - data.name.replace('\'', '"') + `')`, function (err) { + let len = data.content.split(' ').length; + db.run(`INSERT INTO content(contentText, contentType, submittedBy, contentLength) VALUES('` + + validate(data.content) + `', '` + + validate(data.type) + `', '` + + validate(data.name) + `', '` + + len + `')`, function (err) { if (err) { return console.log(err.message); } - console.log(`A row has been inserted with rowid ${this.lastID}`); }); + data.contentLength = len; + console.log(data); + console.log(`New item entered! Row ID: ${this.lastID}`); }); response.writeHead(200, "OK", {'Content-Type': 'text/plain'}); response.end()