Cannot get previous hash of the block when pushing new block - javascript

I created an implementation of Blockchain, it worked well. Then I wanted to write a program that creates a new chain when deleting message.
And instead of getting a new chain where the second block has data from the latest block from the previous chain it drops an error, and I really don't understand what that means. Why "1"? It is even not hash but index of the block in a new chain. Here is error:
Uncaught TypeError: Cannot create property 'prevHash' on number '1'
at Chain.addBlock (chain.js:24)
at newChain (test.js:26)
at HTMLButtonElement.onclick (index.html:1)
Can somebody explain why? I also attached a code snippet for showing hot it all works
// Chain.js
class Block {
constructor(id, data, prevHash = ''){
this.id = id;
this.prevHash = this.prevHash;
this.hash = this.calcHash();
this.data = data;
}
calcHash() {
return CryptoJS.SHA512(this.id + JSON.stringify(this.data)).toString();
}
}
class Chain {
constructor(){
this.chain = [this.genesisBlock()];
}
genesisBlock(){
return new Block(0,'Chain started.');
}
getLastBlock(){
return this.chain[this.chain.length - 1];
}
addBlock(block){
block.prevHash = this.getLastBlock().hash;
block.hash = block.calcHash();
this.chain.push(block)
}
isValid(){
for(let i = 1; i < this.chain.length; i++){
let prev = this.chain[i-1], current = this.chain[i];
if(current.hash !== prev.prevHash || current.hash !== current.calcHash())
return false;
}return true;
}
}
// Msg.js
class Msg {
constructor(msg, date){
this.msg = msg;
const D = new Date();
this.date = [D.getHours(), D.getMinutes(), D.getSeconds()].join(' : ');
}
}
// Test.js
FROZENCHAINS = [];
CHAIN = new Chain();
i = 0;
msg = () => {
let text = $('input').val();
i++;
CHAIN.addBlock(new Block(i, text));
let msg = JSON.stringify(CHAIN.chain,null, 4);
$('#log').text(msg);
let thisMSG = new Msg(text);
$('section').append('<div class="notification is-primary"><span class="tag">' + thisMSG.msg + '</span>'
+ '<span class="tag">Created at: ' + thisMSG.date + '</span><button onclick="$(this).parent().hide() && newChain()" align=center class="delete is-large"></button></div>')
}
newChain = () => {
FROZENCHAINS.push(CHAIN);
delete CHAIN;
CHAIN = new Chain();
CHAIN.addBlock(1,'Hi')
}
.input {
margin: 10px 0;
}
.tag {
font-size: 23px !important;
background-color: whitesmoke !important;
margin: 5px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" charset="utf-8"></script>
<title>Blockchain Chat</title>
</head>
<body>
<div class="tile is-parent">
<article class="tile is-child notification">
<p class="title">Blockchain Chat Part 1</p>
<div class="content">
<pre class="hero-body" id=log></pre>
<section class="hero-body"></section>
<input class="input" value="Hello World"/>
<button onclick="msg()" class="button">Send Message</button>
</div>
</body>
</html>

Your addBlock function expects a Block, but you provide 1 in newChain(). Change the line to
CHAIN.addBlock(new Block(1,'Hi'));

Related

Get the value of the variable outside the function to create a file

I would like to know how I can return the name of the folder created in the folder variable outside the createOrGetFolder function, the intention is to be able to create a file with the same name as the folder created, in this code here:
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(folder, data);
This is my complete code from the .gs file:
/**
* Modified script written by Tanaike and CharlesPlucker
*
* Additional Script by Tyrone
* version 20.01.2023.1
*/
function doGet(e) {
return HtmlService.createTemplateFromFile('forms0101.html').evaluate();
}
function getOAuthToken() {
return ScriptApp.getOAuthToken();
}
function getParent(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var id = ss.getId();
var parent = DriveApp.getFileById(id).getParents().next().getId();
return parent
}
function getLimitFolder(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var pastapai = DriveApp.getFileById(ss.getId()).getParents();
var limitfolder = pastapai.next().getFoldersByName("_").next().getId();
return limitfolder
}
/**
* creates a folder under a parent folder, and returns it's id. If the folder already exists
* then it is not created and it simply returns the id of the existing one
*/
function createOrGetFolder(folderName, parentFolderId) {
try {
var parentFolder = DriveApp.getFolderById(parentFolderId), folder;
if (parentFolder) {
var foldersIter = parentFolder.getFoldersByName("Video");
if (foldersIter.hasNext()) {
var videoFolder = foldersIter.next();
var nextFolderName = folderName + "-01";
while (!folder) {
video_folder = videoFolder.getFoldersByName(nextFolderName);
if (video_folder.hasNext()) {
folder = video_folder.next();
var files = folder.getFiles();
if (files.hasNext()) {
var [a, b] = nextFolderName.split("-");
nextFolderName = `${a}-${String(Number(b) + 1).padStart(2, "0")}`;
folder = null;
}
} else {
folder = videoFolder.createFolder(nextFolderName);
}
}
} else {
folder = parentFolder.createFolder("Video");
folder = folder.createFolder(folderName);
}
} else {
throw new Error("Parent Folder with id: " + parentFolderId + " not found");
}
return folder.getId();
} catch (error) {
return error;
}
}
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile("Sample.csv", data);
// NOTE: always make sure we use DriveApp, even if it's in a comment, for google to import those
// libraries and allow the rest of the app to work. see https://github.com/tanaikech/Resumable_Upload_For_WebApps
Note that in const saveDataAsCSV the currently file creation name is Sample.csv, and this is where I want to apply the folder variable that is in the function createOrGetFolder(folderName, parentFolderId)
And this is the complete code of the HTML file:
/**
* Modified script written by Tanaike and CharlesPlucker
*
* Additional Script by Tyrone
* version 20.01.2023.1
*/
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drive Multi Large File Upload</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
#import url('https://fonts.googleapis.com/css2?family=Rubik:wght#400;600;700&display=swap');
.disclaimer{
width: 480px;
color: #646464;
margin: 20px auto;
padding:0 16px;
text-align:center;
font:400 12px Rubik,sans-serif;
}
h5.center-align.teal-text {
font:700 26px Rubik,sans-serif;
color: #00F498!important;
}
.row {
font:600 14px Rubik,sans-serif;
}
.btn {
background-color: black;
}
.btn:hover {
background-color: #00F498;
}
body {
margin-top: -40px;
}
#progress {
color: #00000;
}
.disclaimer a{
color: #00BCAA;
}
#credit{
display:none
}
</style>
</head>
<body>
<form class="main" id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">
<div id="forminner">
<h5 class="center-align teal-text" style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">YOUR NAME</h5>
<div class="row">
<div class="input-field col s12">
<input id="name01" type="text" name="Name" class="validate" required="required" aria-required="true">
<label for="name" class="">Name</label>
</div>
</div>
<h5 class="center-align teal-text" style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">SOME DESCRIPTION</h5>
<div class="row">
<div class="input-field col s12">
<input id="description" type="text" name="Description" class="validate" required="required" aria-required="true">
<label for="name">Description</label>
</div>
</div>
<div class="row">
<div class="col-8 col-md-4">
<h6>Model</h6>
<select class="custom-select" id="Model">
<option selected="">Choose...</option>
<option value="01">01</option>
<option value="02">02</option>
<option value="03">03</option>
</select>
<h6>Color</h6>
<select class="custom-select" id="Color">
<option selected="">Choose...</option>
<option value="Red">Red</option>
<option value="Green">Green</option>
</select>
</div>
</div>
<div class="row">
<div class="col s12">
<h5 class="center-align teal-text">Upload the Video File</h5>
</div>
</div>
<div class="row">
<div class="file-field input-field col s12">
<div id="input-btn" class="btn">
<span>File</span>
<input id="files" type="file" single="">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Select the file">
</div>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<button id="submit-btn" class="waves-effect waves-light btn submit-btn" type="submit" onclick="submitForm(); return false;">Submit</button>
</div>
</div>
<div class="row">
<div class="input-field col s12 hide" id="update">
<hr>
<p>
Por favor, aguarde enquanto seu arquivo está sendo carregado.<br><span style="color: #00000;"><b>Não feche ou atualize a janela durante o upload.</b></span>
</p>
</div>
</div>
<div class="row">
<div class="input-field col s12" id="progress">
</div>
</div>
</div>
</div>
<div id="success" style="display:none">
<h5 class="center-align teal-text">Tudo certo!</h5>
<p>Se você já preencheu todos os campos é só fechar essa janela e clicar em enviar!</p>
<button id="fechar" class="waves-effect waves-light btn submit-btn" style ="transform: translateX(160%);" type="button" onclick="google.script.host.close()">Fechar</button>
</div>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/js/materialize.min.js"></script>
<script src="https://gumroad.com/js/gumroad.js"></script>
<script>
var upload_folder = "01";
const chunkSize = 5242880;
const uploadParentFolderId = <?=getParent()?>; // creates a folder inside of this folder
const limitfolder = <?=getLimitFolder()?>;
function closer(){
google.script.host.close();
}
function submitForm() {
// Added the below script.
if ($('#submit-btn.disabled')[0]) return; // short circuit
var name = upload_folder
var files = [...$('#files')[0].files]; // convert from FileList to array
if (files.length === 0) {
showError("Por favor, selecione um arquivo");
return;
}
var name = $('#name01').val();
var description = $('#description').val();
var model = $('#Model').val();
upload_folder = model;
var color = $('#Color').val();
var form_values = [name, description, model, color];
form_values = form_values.map(r => r.replaceAll(",", "#")); // Essa linha substitui todas as "," por "#" antes de gerar o .csv
var data = form_values.join(",");
google.script.run.saveDataAsCSV(data, uploadParentFolderId);
google.script.run.saveDataAsCSV(data, limitfolder);
disableForm(); // prevent re submission
// the map and reduce are here to ensure that only one file is uploaded at a time. This allows
// the promises to be run sequentially
files.map(file => uploadFilePromiseFactory(file))
.reduce((promiseChain, currentTask) => {
return promiseChain.then(currentTask);
}, Promise.resolve([])).then( () => {
console.log("Completed all files upload");
showSuccess();
});
}
function disableForm() {
$('#submit-btn').addClass('disabled');
$('#input-btn').addClass('disabled');
$('#update').removeClass('hide');
$('#update').removeClass('hide');
}
function uploadFilePromiseFactory(file) {
return () => {
console.log("Processing: ", file.name);
return new Promise((resolve, reject) => {
showProgressMessage("Seu arquivo está sendo carregado");
var fr = new FileReader();
fr.fileName = file.name;
fr.fileSize = file.size;
fr.fileType = file.type;
// not sure of a better way of passing the promise functions down
fr.resolve = () => resolve();
fr.reject = (error) => reject(error);
fr.onload = onFileReaderLoad;
fr.readAsArrayBuffer(file);
});
};
}
/**
* Gets called once the browser has loaded a file. The main logic that creates a folder
* and initiates the file upload resides here
*/
function onFileReaderLoad(onLoadEvent) {
var fr = this;
var newFolderName = upload_folder
createOrGetFolder(newFolderName, uploadParentFolderId).then(newFolderId => {
console.log("Found or created guest folder with id: ", newFolderId);
uploadFileToDriveFolder.call(fr, newFolderId).then(() => {
fr.resolve();
}, (error) => {
fr.reject(error);
});
},
(error) => {
if (error) {
showError(error.toString());
}
console.log("onFileReaderLoad Error2: ", error);
});
}
/**
* call to the DriveApp api. Wrapped in a promise in case I want to address timing issues between a
* createFolder and findFolderById
*/
function createOrGetFolder(folderName, parentFolderId) {
return new Promise((resolve, reject) => {
google.script.run.withSuccessHandler(response => {
console.log("createOrGetFolder response: ", response);
if (response && response.length) {
resolve(response);
}
reject(response);
}).createOrGetFolder(folderName, parentFolderId);
});
}
/**
* Helper functions modified from:
* https://github.com/tanaikech/Resumable_Upload_For_WebApps
*/
function uploadFileToDriveFolder(parentFolderId) {
var fr = this;
return new Promise((resolve, reject) => {
var fileName = fr.fileName;
var fileSize = fr.fileSize;
var fileType = fr.fileType;
console.log({fileName: fileName, fileSize: fileSize, fileType: fileType});
var buf = fr.result;
var chunkpot = getChunkpot(chunkSize, fileSize);
var uint8Array = new Uint8Array(buf);
var chunks = chunkpot.chunks.map(function(e) {
return {
data: uint8Array.slice(e.startByte, e.endByte + 1),
length: e.numByte,
range: "bytes " + e.startByte + "-" + e.endByte + "/" + chunkpot.total,
};
});
google.script.run.withSuccessHandler(oAuthToken => {
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
xhr.setRequestHeader('Authorization', "Bearer " + oAuthToken);
xhr.setRequestHeader('Content-Type', "application/json");
xhr.send(JSON.stringify({
mimeType: fileType,
name: fileName,
parents: [parentFolderId]
}));
xhr.onload = () => {
doUpload(fileName, {
location: xhr.getResponseHeader("location"),
chunks: chunks,
}).then(success => {
resolve(success);
console.log("Successfully uploaded: ", fileName);
},
error => {
reject(error);
});
};
xhr.onerror = () => {
console.log("ERROR: ", xhr.response);
reject(xhr.response);
};
}).getOAuthToken();
});
}
function showSuccess() {
$('#forminner').hide();
$('#success').show();
$('#fechar').show();
}
function showError(e) {
$('#progress').addClass('red-text').html(e);
}
function showMessage(e) {
$('#update').html(e);
}
function showProgressMessage(e) {
$('#progress').removeClass('red-text').html(e);
}
/**
* Helper functions modified from:
* https://github.com/tanaikech/Resumable_Upload_For_WebApps
*/
function doUpload(fileName, e) {
return new Promise((resolve, reject) => {
showProgressMessage("Carregando: <span style='color: #00F498 ;'>" + "0%</span>");
var chunks = e.chunks;
var location = e.location;
var cnt = 0;
var end = chunks.length;
var temp = function callback(cnt) {
var e = chunks[cnt];
var xhr = new XMLHttpRequest();
xhr.open("PUT", location, true);
console.log("content range: ", e.range);
xhr.setRequestHeader('Content-Range', e.range);
xhr.send(e.data);
xhr.onloadend = function() {
var status = xhr.status;
cnt += 1;
console.log("Uploading: " + status + " (" + cnt + " / " + end + ")");
showProgressMessage("Carregando: <span style='color: #00F498 ;'>"
+ Math.floor(100 * cnt / end) + "%</span>" );
if (status == 308) {
callback(cnt);
} else if (status == 200) {
$("#progress").text("Done.");
resolve();
} else {
$("#progress").text("Error: " + xhr.response);
reject();
}
};
}(cnt);
});
}
/**
* Helper functions modified from:
* https://github.com/tanaikech/Resumable_Upload_For_WebApps
*/
function getChunkpot(chunkSize, fileSize) {
var chunkPot = {};
chunkPot.total = fileSize;
chunkPot.chunks = [];
if (fileSize > chunkSize) {
var numE = chunkSize;
var endS = function(f, n) {
var c = f % n;
if (c == 0) {
return 0;
} else {
return c;
}
}(fileSize, numE);
var repeat = Math.floor(fileSize / numE);
for (var i = 0; i <= repeat; i++) {
var startAddress = i * numE;
var c = {};
c.startByte = startAddress;
if (i < repeat) {
c.endByte = startAddress + numE - 1;
c.numByte = numE;
chunkPot.chunks.push(c);
} else if (i == repeat && endS > 0) {
c.endByte = startAddress + endS - 1;
c.numByte = endS;
chunkPot.chunks.push(c);
}
}
} else {
var chunk = {
startByte: 0,
endByte: fileSize - 1,
numByte: fileSize,
};
chunkPot.chunks.push(chunk);
}
return chunkPot;
}
</script>
</body>
</html>
As folder is without the var prefix, I figured it should work, as in theory this makes it a global variable... however I still get the folder is undefined message in the console.
I also tried calling the function before the file creation code, like this:
createOrGetFolder(folderName, parentFolderId);
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(folder, data);
But that way I get the message folderName is undefined.
Based on the suggestion made in the The WizEd answer's comments, this was my last attempt:
Modified excerpt in the .gs file:
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(newFolderId, data);
Modified excerpt in the HTML file:
var newFolderId = "";
/**
* call to the DriveApp api. Wrapped in a promise in case I want to address timing issues between a
* createFolder and findFolderById
*/
function createOrGetFolder(folderName, parentFolderId) {
return new Promise((resolve, reject) => {
google.script.run.withSuccessHandler(response => {
console.log("createOrGetFolder response: ", response);
if (response && response.length) {
resolve(response);
}
reject(response);
}).createOrGetFolder(folderName, parentFolderId);
newFolderId = createOrGetFolder(folderName, parentFolderId);
});
}
That way I still can't get the name of the folder created or used...
Where am I going wrong?
Global variable are not persistent. What that means is when a function is executed the instance creates the global variable but releases it when the function or function chain finishes.
Here func1() calls func2() so the instance of the global variable is perserved.
However if I run func2() by itself following running func1() it is reset to blank
Run func1()
var globalVariable = "";
function func1 () {
try {
console.log("globalVariable in func1 = ["+globalVariable+"]")
globalVariable = "Hello";
func2();
console.log("globalVariable from func2 = ["+globalVariable+"]")
}
catch(err) {
console.log("Error in func1: "+err);
}
}
function func2 () {
try {
console.log("globalVariable in func2 = ["+globalVariable+"]")
globalVariable = "Good bye";
}
catch(err) {
console.log("Error in func2: "+err);
}
}
11:53:15 AM Notice Execution started
11:53:17 AM Info globalVariable in func1 = []
11:53:17 AM Info globalVariable in func2 = [Hello]
11:53:17 AM Info globalVariable from func2 = [Good bye]
11:53:16 AM Notice Execution completed
Now run func2()
11:57:38 AM Notice Execution started
11:57:39 AM Info globalVariable in func2 = []
11:57:39 AM Notice Execution completed
To perserve the value of globalVariable from one execution to the next you need to use PropertyService
You have created a web application using Google Apps Script. The client-side code calls the server side function createOrGetFolder. You want that this function returns the name of the Folder object assigned to the folder variable.
Currently the server side function createOrGetFolder on success returns the folder id (return folder.getId();).
To get the folder name you could use folder.getName() but changing the return of this function implies to make changes to the client-side code.
One option is to add a a client function to get the folder name. This could be done by calling a server side function using the folder id that currently returns createOrGetFolder. Another way is, to make that createOrGetFolder store the folder name using the Properties Service, the Cache Service, or other store to save the folder name then using a client side function retrieve this value. In both options is very likely that the changes to the html / gs files will be minimal but this will not deliver an optimal overall performance of your web application as Google Apps Script services are slow.
Another option is to change the createOrGetFolder function return but that implies investing time on studying the client side code and changing multiple lines of code, probably will be more expensive in programmer hours than the first option but might warrant that your web application will have an optimal overall permorance by keeping the calls to the Google Apps Script services at minimum.
Resources
https://developers.google.com/apps-script/guides/html/communication
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Using PokeAPI to fetch data. Can't figure out why span element is not updating

So I'm using the PokeAPI to fetch the name of a Pokemon, then shuffling that name, and the user is supposed to guess what it is in the input. If they don't know then they can click the next button and it reshuffles a new mon. If they guess right they can press the same next button for a new mon. Each time they guess right the score increases by 1. That's working but I cant figure out why the out of/total games span isn't updating as well. Please excuse my terrible attempt at JS I'm very new if you can help me make my code look better that would be great.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" href="style.css" />
<title>Who's that Pkmn?</title>
</head>
<body>
<header>
<h1>Who's that Pokemon?!</h1>
</header>
<div id="jumble">?????</div>
<div class="container">
<input id="guess" type="text" placeholder="enter pkmn name" />
<button id="submit" class="btn" type="submit">go</button>
<button id="next" class="btn">next</button>
<p id="msg">unshuffle the letters</p>
</div>
<div id="scorekeepers">
<p>Score: <span id="score">0</span>
out of: <span id="gamesPlayed">0</span></p>
</div>
<script src="script.js"></script>
</body>
</html>
let jumbledName = document.querySelector("#jumble");
let guessInput = document.querySelector('#guess')
let submitButton = document.querySelector('#submit')
let nextButton=document.querySelector('#next')
let messageDisplay = document.querySelector('#msg')
let score = document.querySelector('#score')
let gamesPlayed = document.querySelector('#gamesPlayed')
score = 0;
gamesPlayed = 0;
let getPokemonName = function() {
fetch(`https://pokeapi.co/api/v2/pokemon/${Math.floor(Math.random()*151+1)}/`)
.then(function(response) {
return response.json();
})
.then(function(data) {
const pokeName = data.name;
const pokeNameJumbled = pokeName.shuffle();
displayInfomation(pokeName, pokeNameJumbled);
});
};
getPokemonName();
guessInput.value=''
// pokeNameJumbled=''
const displayInfomation = function(name, jumbledName) {
pokeName = name;
pokeNameJumbled = jumbledName;
jumble.textContent = jumbledName;
};
const displayMessage = function(message) {
document.querySelector("#msg").textContent = message;
};
const checkName = function () {
document.querySelector("#guess").textContent = guessInput;
const guess = document.querySelector("#guess").value.toLowerCase();
if (!guess) {
displayMessage("No guess entered!");
} else if (guess === pokeName) {
displayMessage(`Thats correct! It's ${pokeName}!`)
score++
document.querySelector("#score").textContent = score;
guessInput.value=''
} else if (guess != pokeName) {
displayMessage(`Wrong!`);
document.querySelector("#gamesPlayed").textContent = gamesPlayed;
}
};
submitButton.addEventListener('click', checkName)
nextButton.addEventListener('click',getPokemonName)
String.prototype.shuffle = function() {
var a = this.split(""),
n = a.length;
for (var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
return a.join("");
};

object.style.display throwing message error on console

UPDATE so here is a better code for you. I cut it down to have only what's strictly necessary for you to reproduce the problem.
// change the color of the button when mousedown/up
let button = document.getElementsByClassName('button');
for(i = 0; i < button.length; i++){
button[i].onmousedown = function() {
this.style.backgroundColor = "#a3a3a3";
};
button[i].onmouseup = function() {
this.style.backgroundColor = "#808080";
};
}
let buttonLg = document.getElementsByClassName('button-lg');
for(i = 0; i < buttonLg.length; i++){
buttonLg[i].onmousedown = function() {
this.style.backgroundColor = "#a3a3a3";
};
buttonLg[i].onmouseup = function() {
this.style.backgroundColor = "#808080";
};
}
let button2 = document.getElementsByClassName('button2');
for(i = 0; i < button2.length; i++){
button2[i].onmousedown = function() {
this.style.backgroundColor = "#ffe299";
};
button2[i].onmouseup = function() {
this.style.backgroundColor = "#ffca47";
};
}
// show the numbers typed or result
let result = document.getElementById('result');
let preview = [];
let buttonAc = document.getElementById('ac');
let plusMinus = document.getElementById('plus-minus');
let plusMinus2 = document.getElementById('plus-minus2');
let buttonN7 = document.getElementById('seven');
let buttonN72 = document.getElementById('seven2');
let buttonN8 = document.getElementById('eight');
let buttonN82 = document.getElementById('eight2');
let buttonN9 = document.getElementById('nine');
let buttonN92 = document.getElementById('nine2');
// button AC erasing result and changing outlook to C when other buttons are clicked
// number 0 disapear when there is only zero and when a key is clicked
buttonAc.onclick = function () {
buttonAc.innerHTML = "AC";
preview = [];
result.innerHTML = 0;
}
// concatenation of the buttons numbers without any commas
buttonN7.onclick = function () {
document.getElementById('ac').innerHTML = "C";
buttonN7 = 7;
preview.push(buttonN7);
const a = preview.join('');
result.innerHTML = a;
}
buttonN8.onclick = function () {
document.getElementById('ac').innerHTML = "C";
buttonN8 = 8;
preview.push(buttonN8);
const a = preview.join('');
result.innerHTML = a;
}
buttonN9.onclick = function () {
document.getElementById('ac').innerHTML = "C";
buttonN9 = 9;
preview.push(buttonN9);
const a = preview.join('');
result.innerHTML = a;
}
// positive to negative value and vice versa with the plus, minus key
plusMinus.onclick = function(){
let a = preview.join('');
let b = parseInt(a, 10);
let c = b * -1;
result.innerHTML = c;
plusMinus.style.display = "none";
plusMinus2.style.display = "block";
//this code below works
//document.getElementById('nine').style.display = "none";
//that one does not work
buttonN9.style.display = "none";
buttonN92.style.display = "block";
}
plusMinus2.onclick = function(){
let a = preview.join('');
let b = parseInt(a, 10);
let c = b * -1;
result.innerHTML = b;
plusMinus2.style.display = "none";
plusMinus.style.display = "block";
}
buttonN92.onclick = function(){
result.innerHTML = 0;
preview = [];
//this code below works
//document.getElementById('nine').style.display = "block";
//that one does not work
buttonN9.style.display = "block";
buttonN92.style.display = "none";
}
h1 {
padding: 30px;
}
.result {
font-size: 80px;
border: 2px solid #000;
color: #f9f9f9;
padding-right: 20px;
background-color: #696969;
}
.row1 {
border: 2px solid #000;
}
.button,
.button2,
.button-lg {
width: 25%;
}
p {
cursor: pointer;
}
#ac,
#plus-minus,
#plus-minus2,
#percentage,
#seven,
#eight,
#nine,
#seven2,
#eight2,
#nine2 {
font-size: 40px;
background-color: #808080;
color: #f9f9f9;
height: 140px;
padding-top: 50px;
margin-bottom: 0px;
border-right: 1px solid #696969;
}
#ac,
#plus-minus,
#percentage,
#seven,
#eight,
#nine {
display: block;
}
#plus-minus2,
#seven2,
#eight2,
#nine2 {
display: none;
}
#division,
#multiplication{
font-size: 40px;
background-color: #ffca47;
color: #f9f9f9;
height: 140px;
margin-bottom: 0px;
padding-top: 50px;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<title>Calculator</title>
</head>
<body>
<h1 class="text-center">Calculator</h1>
<div class="container">
<div class="row">
<div class="col-xl-12">
<h5 id="result" class="card-title text-right result">0</h5>
</div>
</div>
<div class="row">
<div class="col-xl-12">
<div class="card-group row1">
<p id="ac" class="card-text text-center button">AC</p>
<p id="plus-minus" class="card-text text-center button">+ | -</p>
<p id="plus-minus2" class="card-text text-center button">+ | -</p>
<p id="percentage" class="card-text text-center button">%</p>
<p id="division" class="card-text text-center button2">/</p>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-12">
<div class="card-group row2">
<p id="seven" class="card-text text-center button">7</p>
<p id="seven2" class="card-text text-center button">7</p>
<p id="eight" class="card-text text-center button">8</p>
<p id="eight2" class="card-text text-center button">8</p>
<p id="nine" class="card-text text-center button">9</p>
<p id="nine2" class="card-text text-center button">9</p>
<p id="multiplication" class="card-text text-center button2">X</p>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="script.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
So here is the idea: It's a calculator, so I opened my calculator from my iMac and I just checked the behavior to try to reproduce it.
What you can do:
- clicking on the number, showing them on the screen result.
- Clicking on AC make the button change to C and clear the screen result.
- Clicking on %, / and X makes nothing yet.
- Clicking on "+|-" swap the number to it's minus.
Here is what I'm trying to do. Normally, when you click the "+|-" key several time, it's going from negative to positive etc. ... and, normally if you click on a number it should go back to 0. I tried first with an external button to test and then I used the key 9 to see if it will work fine, with that code here:
document.getElementById('nine').style.display = "block"; //or none
It works perfectly! But, when I throw that code instead (I marked them up on the whole snippet to you identify them better)
buttonN9.style.display = "block"; //or none
BADABOOM, red alert on the console.
I've tried everything on every sens since hours; return my brain as much as I can; I'm still doing it. This evening, I must go somewhere with my wife. It's going to be hard to not think about it.
I will be honest with you, the calculator from the iPhone does not have the same behavior. If you fire 1,2,3 then +|- it goes -123. If you fire again 7, it goes -1237. So, maybe I mess up my brain too much, but I want to do it :)
Simply put all your code that accesses the DOM in a window.onload() handler, like this:
<script>
window.onload = function() {
let plusMinus = document.getElementById('plus-minus');
let buttonN9 = document.getElementById('nine');
// other variable initialization
plusMinus.onclick = function() {
buttonN9.style.display = "none";
// other handler code logic
}
// other event handlers
};
</script>
This way, you are sure the DOM is ready before accessing or manipulating it.
Also, from the html file in your Github repository here; you included several external scripts (including jquery) after your script. Except these scripts depend on your own script - which is not likely, you should always place your own script last after other external scripts that your code may likely depend on.
UPDATE: There are issues with firing window.onload from within the body tag. see the answer on this post. In addition to my previous answer, try putting your scripts in the header tag like this:
<head>
<!-- Place other external scripts here -->
<script src='myscript.js'></script>
</head>
So, here is the solution. As a beginner, i made first some mistakes, i was repeating the functions. I've created for each behavior one function. The main problem is i wanted to get to an index thru an array without using any loop. The loop for made the deal and now it's behaving as i want ...
The list of the buttons did not change. What you have:
- A function to handle a reset button.
- A function to handle the value of the numbers buttons
- A function to the positive/negative number
- A function to reset the positive/negative number in case you click on one number.
This is how behave the calculator that i have so i'm just trying to do the exactly same thing. Thanks a lot for your help !
function buttonResetHandler(button){
button.onclick = function (){
button.innerHTML = "AC";
preview = [];
result.innerHTML = 0;
}
}
buttonResetHandler(buttonAc);
// concatenation of the buttons numbers without any commas
function buttonNumberHandler(button, value){
button.onclick = function(){
buttonAc.innerHTML = "C";
button = value;
preview.push(button);
const a = preview.join('');
result.innerHTML = a;
}
}
buttonNumberHandler(buttonN0, 0);
buttonNumberHandler(buttonN1, 1);
buttonNumberHandler(buttonN2, 2);
buttonNumberHandler(buttonN3, 3);
buttonNumberHandler(buttonN4, 4);
buttonNumberHandler(buttonN5, 5);
buttonNumberHandler(buttonN6, 6);
buttonNumberHandler(buttonN7, 7);
buttonNumberHandler(buttonN8, 8);
buttonNumberHandler(buttonN9, 9);
// positive to negative value and vice versa with the plus, minus key
function buttonPlusMinusHandler(button1, button2, button3, button4){
button1.onclick = function(){
let a = preview.join('');
let b = parseInt(a, 10);
let c = b * -1;
result.innerHTML = c;
button1.style.display = "none";
button2.style.display = "block";
for(i = 0; i < button3.length; i++){
button3[i].style.display = "none";
}
for(i = 0; i < button4.length; i++){
button4[i].style.display = "block";
}
if(result.innerHTML == c){
button2.onclick = function(){
result.innerHTML = b;
button2.style.display = "none";
button1.style.display = "block";
}
}
}
}
buttonPlusMinusHandler(plusMinus, plusMinus2, allButtonsN, allButtonsN2);
function buttonNumberResetPlusMinus(button, button2){
for(i = 0; i < button.length; i++){
button[i].onclick = function(){
preview = [];
result.innerHTML = 0;
for(i = 0; i < button2.length; i++){
button[i].style.display = "none";
button2[i].style.display = "block";
}
}
}
}
buttonNumberResetPlusMinus(allButtonsN2, allButtonsN);

Simple HTML & JavaScript shell game

Locked. There are disputes about this question’s content being resolved at this time. It is not currently accepting new answers or interactions.
I have been working on this for a couple of days and I have come to the point of trying to figure out why this is not displaying the message of winning or losing. This is a simple shell game using just HTML, JavaScript and a small CSS file. Thinking it is the JavaScript I am having problems with I have taken out so many line now I'm kinda lost. Any push to the right direction would be great.
var noOfShells;
var noOfShells = 3;
var looplimit = noOfShells + 1;
function ballShell(shells) {
var ballLoc = (Math.floor(Math.random() * shells)) + 1;
return ballLoc;
}
document.getElementById('elTwo').innerHTML = ballShell();
var ballIs = ballShell(noOfShells);
function newShell(newID, ballIs) {
this.shellId = newID;
this.shellName = "Shell" + newID;
this.ballIn = ballIs;
this.hasBall = function() {
var theId = newID;
var theBall = ballIs;
var checkMsg = "My number is " + theId + ". The ball is in shell " + theBall;
if (theId === theball) {
var checkMsg = checkMsg + " You Win! ";
return checkMsg;
} else {
var checkMsg = checkMsg + " You Lose! ";
}
};
}
for (var i = 1; i < 4; i++) {
this["shell" + i] = new newShell(i, ballIs);
}
var shellOneLink = document.getElementById('shellOne');
shellOneLink.addEventListener('click', shell1.hasball(), false);
function reloadPage() {
location.reload();
}
var activateReload = document.getElementById('reloadLink');
activateReload.onclick = reloadPage;
ul {
width: 100%;
text-align: center
}
ul li {
display: inline-block;
width: 200px;
border: solid 1px #ccc;
}
<link rel="stylesheet" type="text/css" href="css/base.css">
<header>
<h1>Shell Game</h1>
<nav>
<ul>
<li>
<a id="shellOne" href="#">
<img src="images/shell.jpg" alt="shell">
</a>
</li>
<li>
<a id="shellTwo" href="#">
<img src="images/shell.jpg" alt="shell">
</a>
</li>
<li>
<a id="shellThree" href="#">
<img src="images/shell.jpg" alt="shell">
</a>
</li>
</ul>
</nav>
</header>
<main>
<h3 id="text-message"> </h3>
</main>
<footer>
<a id="reloadLink" href="index.html">Reload Page</a>
</footer>
You only included your stylesheet and not your javascript source file.
You have to include it like this:
<script src="myscripts.js"></script>
Here is an example of a simple shell game:
var doc = document, bod = doc.body;
function E(id){
return doc.getElementById(id);
}
function ShellGame(displayElement){
this.play = function(shellNum){
var shell = Math.floor(Math.random()*3), r = 'You Lost!';
switch(shellNum){
case 0:
if(shell === 0){
r = 'You Won!';
}
break;
case 1:
if(shell === 1){
r = 'You Won!';
}
break;
case 2:
if(shell === 2){
r = 'You Won!';
}
break;
}
displayElement.innerHTML = r;
}
}
var sg = new ShellGame(E('text-message'));
E('shellOne').onclick = function(){
sg.play(0);
}
E('shellTwo').onclick = function(){
sg.play(1);
}
E('shellThree').onclick = function(){
sg.play(2);
}

Making a javascript hangman game and I can't get my function to apply to repeated letters

Everything about the script works great right now unless there's a repeated letter in the word. If so, then it will only display the first of the letters. For example, if the random word is "look" it would display like this "lo k".
Unfortunately the only other related javascript hangman question here was for a script that didn't actually have issues on repeated letters. For reference: how to deal with repeated letters in a javascript hangman game. Can anyone help me get through the repeated letter issue? Thanks!
My HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/jquery-1.11.2.min.js"></script>
<script src="js/jquery-1.11.2.js"></script>
<link rel="stylesheet" href="css/main.css">
<title>Hang a Blue Devil</title>
</head>
<body>
<div class="wrapper">
<h1 class="title">Hangman</h1>
<h2 class="attempt-title">You have this many attempts left: </h2>
<ul class="hangman-word">
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
</ul>
<h3 class="hangman-letters"></h3>
<input class="text-value" type="text" maxlength="1" onchange="setGuess(this.value)">
<button class="text-button" onclick="checkGuess()"></button>
<p class="letters-guessed"></p>
</div>
</body>
<script src="js/hangman.js"></script>
</html>
My JS:
var hangmanWords = [
"the","of","and","a","to","in","is","you","that","it","he",
"was","for","on","are","as","with","his","they","I","at","be",
"this","have","from","or","one","had","by","word","but","not",
"what","all","were","we","when","your","can","said","there",
"use","an","each","which","she","do","how","their","if","will",
"up","other","about","out","many","then","them","these","so",
"some","her","would","make","like","him","into","time","has",
"look","two","more","write","go","see","number","no","way",
"could","people","my","than","first","water","been","call",
"who","oil","its","now","find","long","down","day","did","get",
"come","made","may","part"
];
// declared variables
var randomNumber = Math.floor(Math.random() * 100);
var randomWord = hangmanWords[randomNumber];
var underscoreCount = randomWord.length;
var underscoreArr = [];
var counter = randomWord.length +3;
var numberTest = 0;
var lettersGuessedArr = [];
var lettersGuessedClass = document.querySelector('.letters-guessed');
var li = document.getElementsByClassName('tester');
var textValue = document.querySelector('.text-value');
var attemptTitle = document.querySelector('.attempt-title');
var hangmanWordClass = document.querySelector('.hangman-word');
var hangmanLettersClass = document.querySelector('.hangman-letters');
// actions
attemptTitle.innerHTML = "You have this many attempts left: " + counter;
console.log(randomWord);
function setGuess(guess) {
personGuess = guess;
}
for (i=0;i<underscoreCount;i+=1) {
underscoreArr.push("_ ");
underscoreArr.join(" ");
var underscoreArrString = underscoreArr.toString();
var underscoreArrEdited = underscoreArrString.replace(/,/g," ");
hangmanLettersClass.innerHTML = underscoreArrEdited;
}
function pushGuess () {
lettersGuessedArr.push(personGuess);
var lettersGuessedArrString = lettersGuessedArr.toString();
var lettersGuessedArrEdited = lettersGuessedArrString.replace(/,/g," ");
lettersGuessedClass.innerHTML = lettersGuessedArrEdited;
}
function checkGuess() {
for (var i=0;i<randomWord.length;i+=1) {
if (personGuess === randomWord[i]) {
console.log(personGuess);
numberTest = i;
li[i].textContent = randomWord[i];
i += 20;
textValue.value= "";
} else if ((randomWord.length - 1) > i ) {
console.log("works");
} else {
pushGuess();
counter -= 1;
attemptTitle.innerHTML = "You have made this many attempts: " + counter;
textValue.value= "";
}
}
};
My bin:
http://jsbin.com/dawewiyipe/4/edit
You had a stray bit of code that didn't belong:
i += 20;
I took it out, and the problem went away (the loop was intended to check each character, the +=20 broke the process of checking each character)
function checkGuess() {
for (var i=0;i<randomWord.length;i+=1) {
if (personGuess === randomWord[i]) {
console.log(personGuess);
numberTest = i;
li[i].textContent = randomWord[i];
textValue.value= "";
} else if ((randomWord.length - 1) > i ) {
console.log("works");
} else {
pushGuess();
counter -= 1;
attemptTitle.innerHTML = "You have made this many attempts: " + counter;
textValue.value= "";
}
}
}
http://jsbin.com/noxiqefaji/1/edit

Categories

Resources