diff --git a/README.md b/README.md index 3945c152..ea922fef 100755 --- a/README.md +++ b/README.md @@ -1,93 +1,28 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js -=== +View at a2-raymonddolan.glitch.me -Due: September 16th, by 11:59 PM. +## Raymond's Initiative Tracker +My application is a simple initiative tracker for Dungeons and Dragons or a similar tabletop roleplaying game. In those games players and enemies take turns, so keeping track of the turn order is helpful. -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. +I used flex for my title and grid for position of the rest of the elements. -Baseline Requirements ---- +The derived field is optional. You can either enter the initiative that a player rolled when the 'roll?' box is not checked, or you can roll it in the application. If 'roll' is checked, the form asks for the initiative bonus, which is a positive or negative number. Ex. Jill Holyhand the monk may be quick and have a +4 to initiative, while Grognas Flatfoot may have a -2. When entering in this way the app takes your bonus, rolls a d20, and adds your bonus to get your initiative number. -Note that there is a very large range of application areas and possibilities that meet these baseline requirements. Make your application do something useful! A todo list, storing / retrieving high scores for a very simple game, have a little fun with it. +Additionally, the arrows on the end of creatures are used to resolve ties. If two creatures have the same initiative, then the up and down arrows can be used to position them correctly (in game the tie can be resolved a number of ways, either saying the creature with the higher initiative bonus goes first, the player goes first, or through rock paper scissors). -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 `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 `
+ + + + + + + + + + +
IdNameInitiativeHPAC
+ - diff --git a/public/js/scripts.js b/public/js/scripts.js index de052eae..641d87f7 100755 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,150 @@ -// Add some Javascript code here, to run on the front end. -console.log("Welcome to assignment 2!") \ No newline at end of file +const processJSON = (json) => { + //console.log(json) + const creatures = document.getElementById("creatures"); + creatures.innerHTML = ""; + json.forEach((creature) => { + creatures.innerHTML += ` + + ${ + creature.id + } + ${ + creature.name} + + ${ + creature.num} + + ${ + creature.hp} + + ${ + creature.ac} + + + + + + + + ` + }) +} + const submit = function( e ) { + // prevent default form action from being carried out + e.preventDefault() + + const checkbox = document.getElementById('roll') + const initbonus = document.querySelector( '#initbonus') + const initiative = document.querySelector( '#initiative' ) + + const name = document.querySelector( '#name' ) + const hp = document.querySelector( '#hp') + const ac = document.querySelector( '#ac') + if(name.value === "" || ac.value === "" || + initbonus.value === "" || hp.value === "") { + window.alert("Please fill in all of the fields.") + return false + } + + // handle initiative + var init; + if(checkbox.checked) { + // Roll their initiative + + init = parseInt(initbonus.value) + getRandomInt(20) + + } else { + // their initiative is what was entered. + init = initiative.value + } + + const json = {name: name.value, num: init, ac:ac.value, hp:hp.value } + const body = JSON.stringify( json ) + //console.log(json) + + fetch( '/submit', { + method:'POST', + body + }) + .then( response => response.json()) + .then( resjson => processJSON(resjson)) + + // clear inputs + name.value = "" + initiative.value = 0 + initbonus.value = 0 + hp.value = 0 + ac.value = 0 + + return false + } + +const removeCreature = function (e) { + e.preventDefault(); + const id = e.target.getAttribute("id"); + + const json = {delete: 'delete', id}, + body = JSON.stringify(json); + + fetch('/submit', { + method:'POST', + body, + }) + .then(response => response.json()) + .then( resjson => processJSON(resjson)) +} + +const moveCreature = function (e, dir) { + e.preventDefault(); + const id = e.target.getAttribute("id"); + + const json = {movedir: dir, id}, + body = JSON.stringify(json); + + fetch('/submit', { + method:'POST', + body, + }) + .then(response => response.json()) + .then( resjson => processJSON(resjson)) +} + + window.onload = function() { + const button = document.querySelector( 'button' ) + button.onclick = submit + + fetch('/appData') + .then(response => response.json()) + .then(json => processJSON(json)); + } + + document.addEventListener("click", function (e) { + if(e.target && e.target.classList[0] == "delete") { + removeCreature(e) + } + if(e.target && e.target.classList[0] == "moveUp") { + moveCreature(e, -1) + } + if(e.target && e.target.classList[0] == "moveDown") { + moveCreature(e, 1) + } + }) + + function switchRoll() { + var checkBox = document.getElementById("roll"); + var bonus = document.getElementById("initbonusBox") + var init = document.getElementById("initiativeBox") + if(checkBox.checked == true) { + init.style.display = "none" + bonus.style.display = "block" + } else { + bonus.style.display = "none" + init.style.display = "block" + + } + } + + function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)) + 1 + } \ No newline at end of file diff --git a/server.improved.js b/server.improved.js index 26673fc0..418364c3 100644 --- a/server.improved.js +++ b/server.improved.js @@ -6,10 +6,10 @@ const http = require( 'http' ), dir = 'public/', port = 3000 +var id = 1 +// Storage, storing players from highest -> lowest initiative const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} + { 'name': 'Drathaniel', 'num': '10', 'ac': '13', 'hp': '37', 'id': 0} ] const server = http.createServer( function( request,response ) { @@ -25,7 +25,14 @@ const handleGet = function( request, response ) { if( request.url === '/' ) { sendFile( response, 'public/index.html' ) - }else{ + } + else if(request.url === '/appData') { + response.writeHead( 200, "OK", {'Content-Type': 'application/json'}) + response.write(JSON.stringify(appdata)) + response.end() + + } + else{ sendFile( response, filename ) } } @@ -38,15 +45,73 @@ const handlePost = function( request, response ) { }) request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) - - // ... do something with the data here!!! - + //console.log( JSON.parse( dataString ) ) + + json = JSON.parse( dataString) + + // delete + if(json.hasOwnProperty('delete')) { + appdata.splice(appdata.findIndex(x => x.id.toString() === json.id.toString()), 1); + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.write(JSON.stringify(appdata)) + response.end() + return false; + } + // move + if(json.hasOwnProperty('movedir')) { + var oldpos = appdata.findIndex(x => x.id.toString() === json.id.toString()) + var newpos = oldpos + parseInt(json.movedir) + // If they try to move outside the bounds, return + if( newpos < 0 || newpos >= appdata.length) { + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.write(JSON.stringify(appdata)) + response.end() + return false; + } + + var start = appdata[oldpos] + var end = appdata[newpos] + // If it would mess up order refuse + if(parseInt(start['num']) !== parseInt(end['num'])) { + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.write(JSON.stringify(appdata)) + response.end() + return false; + } + array_move(oldpos, newpos) + //console.log(appdata) + + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.write(JSON.stringify(appdata)) + response.end() + return false; + } + + json['id'] = id++ + //console.log(json) + + var pos = determineOrder( parseInt(json.num)) + + appdata.splice(pos, 0, json) + + //console.log(appdata) response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.write(JSON.stringify(appdata)) response.end() }) } +function determineOrder ( initiative ) { + var i; + for(i=0; i parseInt(appdata[i].num)) { + return i + } + } + return i + +} + const sendFile = function( response, filename ) { const type = mime.getType( filename ) @@ -69,4 +134,14 @@ const sendFile = function( response, filename ) { }) } +function array_move( old_index, new_index) { + if (new_index >= appdata.length) { + var k = new_index - appdata.length + 1; + while (k--) { + appdata.push(undefined); + } + } + appdata.splice(new_index, 0, appdata.splice(old_index, 1)[0]); +}; + server.listen( process.env.PORT || port )