Continuous calculations on-the-go in Javascript - javascript

I have a simple calculator made in JS and works well, but when I finish my calculation and get my result (i.e. pressing the "=" button), I want the returned result to be in a saved state. The "=" button must be ready to be clicked again, and get the same result, and then add it to the save result.
I've tried many obvious ways, such as adding the result to itself (which doesn't work because the result will be multiplied by itself, not added), exploiting the eval() function and adding a "+" string to the end result, etc.
TLDR: If you go to any calculator program, and type "2+2" and click equals, you get 4. If you click equals again, you get 6 ("2+2+2").
Here's my current code:
var disp = document.getElementById("calc-output"),
acceptedInputs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, "-", ".", "+", "*", "/"];
function ud(n) {
if (acceptedInputs.includes(n)) {
if (disp.innerHTML.length == 31) {
disp.innerHTML = 0;
}
if (disp.innerHTML.length < 18) {
if (disp.innerHTML != 0) {
disp.innerHTML += n;
} else if (acceptedInputs.slice(11, -1).includes(n)) {
disp.innerHTML = 0 + n;
} else if (disp.innerHTML.toString().slice(1, 2) == ".") {
if (disp.innerHTML.toString().split(".").length-1 <= 1) {
disp.innerHTML += n;
}
} else {
disp.innerHTML = n;
}
}
}
}
function answer() {
if (eval(disp.innerHTML) == disp.innerHTML) {
disp.innerHTML = disp.innerHTML + "+"
}
c = eval(disp.innerHTML);
disp.innerHTML = c;
}
function clear() {
disp.innerHTML = "0";
}
function back() {
var str = disp.innerHTML.toString();
if (disp.innerHTML != 0 || str.charAt(1) == ".") {
if (str.length >= 2) {
str = str.slice(0, -1);
disp.innerHTML = str;
} else {
disp.innerHTML = 0;
}
}
}
document.getElementById("n1").addEventListener("click", function() {
ud(1);
});
document.getElementById("n2").addEventListener("click", function() {
ud(2);
});
document.getElementById("n3").addEventListener("click", function() {
ud(3);
});
document.getElementById("n4").addEventListener("click", function() {
ud(4);
});
document.getElementById("n5").addEventListener("click", function() {
ud(5);
});
document.getElementById("n6").addEventListener("click", function() {
ud(6);
});
document.getElementById("n7").addEventListener("click", function() {
ud(7);
});
document.getElementById("n8").addEventListener("click", function() {
ud(8);
});
document.getElementById("n9").addEventListener("click", function() {
ud(9);
});
document.getElementById("zero-button").addEventListener("click", function() {
ud(0);
});
document.getElementById("comma-button").addEventListener("click", function() {
ud('.');
});
document.getElementById("plus-button").addEventListener("click", function() {
ud('+');
});
document.getElementById("minus-button").addEventListener("click", function() {
ud('-');
});
document.getElementById("multi-button").addEventListener("click", function() {
ud('*');
});
document.getElementById("div-button").addEventListener("click", function() {
ud('/');
});
document.getElementById("back-button").addEventListener("click", function() {
back();
});
document.getElementById("clear-button").addEventListener("click", function() {
clear();
});
document.getElementById("equals-button").addEventListener("click", function() {
answer();
});

Save the last operation (e.g. " + 2") in some variable when user clicks equal. So, when he clicks again, just append saved value to the string from display.
var lastOperation = null;
function answer() {
if (eval(disp.innerHTML) == disp.innerHTML) {
// apply the last operation to displayed number
disp.innerHTML = disp.innerHTML + (lastOperation ? lastOperation[0] : '');
}
// find the last operation + | - | * | /
lastOperation = disp.innerHTML.split(new RegExp('(\\+|\\-|\\*|/).*$'));
c = eval(disp.innerHTML);
disp.innerHTML = c;
}

Related

How can I account for when the user inputs nothing to Alexa after this.emit(":ask", speech)?

I'm writing a game for Alexa but the user input is required. If there is no answer, then the game should end with a unique message of the user's score. Currently, I have the Alexa prompting the user like this.
this.emit(":ask", speech);
However, if the user chooses not to answer, the Alexa ends without any sort of message. I checked whether I could account for this in the Stop or Cancel handler, but the program doesn't seem to exit that way. How can I account for no input and specify a exit message? Here is my full code for reference.
'use strict';
const Alexa = require('alexa-sdk');
//Messages
const WELCOME_MESSAGE = "Welcome to three six nine. You can play three six nine with just me or with friends. Say help for how to play";
const START_GAME_MESSAGE = "OK. I will start!";
const EXIT_SKILL_MESSAGE = "Better luck next time!";
const HELP_MESSAGE = "Three six nine is a game where we take turns counting up from one. If the number is divisible by three, you're going to say quack. If the number has a three, six, or nine anywhere, you're going to say quack";
const speechConsWrong = ["Argh", "Aw man", "Blarg", "Blast", "Boo", "Bummer", "Darn", "D'oh", "Dun dun dun", "Eek", "Honk", "Le sigh",
"Mamma mia", "Oh boy", "Oh dear", "Oof", "Ouch", "Ruh roh", "Shucks", "Uh oh", "Wah wah", "Whoops a daisy", "Yikes"];
const states = {
START: "_START",
GAME: "_GAME"
};
//Game Variables
var counter = 1;
var numPlayers = 1; //By default is one
const handlers = {
//Game goes straight to play currently
"LaunchRequest": function() {
this.handler.state = states.START;
this.emitWithState("Start");
},
"PlayIntent": function() {
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"PlayWithFriends": function() {
const itemSlot = this.event.request.intent.slots.numFriends.value;
numPlayers = itemSlot;
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"AMAZON.HelpIntent": function() {
this.response.speak(HELP_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"Unhandled": function() {
this.handler.state = states.START;
this.emitWithState("Start");
}
};
//When skill is in START state
const startHandlers = Alexa.CreateStateHandler(states.START,{
"Start": function() {
this.response.speak(WELCOME_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"PlayIntent": function() {
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"PlayWithFriends": function() {
const itemSlot = this.event.request.intent.slots.Item;
numPlayers = itemSlot; //set number to slot value
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"AMAZON.StopIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
this.emit(":responseReady");
},
"AMAZON.CancelIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
this.emit(":responseReady");
},
"AMAZON.HelpIntent": function() {
this.response.speak(HELP_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"Unhandled": function() {
this.emitWithState("Start");
}
});
const gameHandlers = Alexa.CreateStateHandler(states.GAME,{
"Game": function() {
let speech = "";
let turnDivisible = (counter-1) % (numPlayers+1);
if (turnDivisible != 0) {
this.emit(":ask", speech);
} else {
this.emitWithState("NextNumber");
}
},
"NextNumber": function() {
//If the counter is at 1, the game is beginning with Alexa
if (counter == 1) {
this.attributes.response = START_GAME_MESSAGE + " ";
} else {
this.attributes.response = " ";
}
//check if number contains three, six, nine or divisible by nine
let speech = " ";
let divisible = counter % 3;
let counterString = counter.toString();
if (counterString.indexOf('3') > - 1 || counterString.indexOf('6') > - 1 || counterString.indexOf('9') > - 1 || divisible === 0) {
speech = this.attributes.response + "quack";
} else {
speech = this.attributes.response + counter;
}
//update variables
counter++;
this.emit(":ask", speech);
},
"AnswerIntent": function() {
let correct = checkAnswer(this.event.request.intent.slots, counter);
//Game continues when you get the correct value
if (correct) {
counter++;
this.emitWithState("Game");
}
//Game ends when the value is incorrect
else {
let speechOutput = endGame();
this.response.speak(speechOutput);
this.emit(":responseReady");
}
},
"AMAZON.StopIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
endGame();
this.emit(":responseReady");
},
"AMAZON.CancelIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
endGame();
this.emit(":responseReady");
},
"AMAZON.HelpIntent": function() {
this.response.speak(HELP_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"Unhandled": function() {
this.emitWithState("Game");
}
});
function checkAnswer(slots, value)
{
for (var slot in slots) {
if (slots[slot].value !== undefined)
{
let slotValue = slots[slot].value.toString().toLowerCase();
let counterValue = value.toString();
let divisible = value % 3;
if (divisible === 0) {
if (slotValue == "quack") {
return true;
} else {
return false;
}
}
else if (counterValue.indexOf('3') > - 1 || counterValue.indexOf('6') > - 1 || counterValue.indexOf('9') > - 1) {
if (slotValue == "quack") {
return true;
} else {
return false;
}
}
else if (slotValue == value.toString().toLowerCase())
{
return true;
}
else
{
return false;
}
}
}
return false;
}
function endGame() {
let speechOutput = "";
let response = getSpeechCon(false);
response += "your final score is " + counter;
speechOutput = response + ". " + EXIT_SKILL_MESSAGE;
counter = 1;
numPlayers = 1;
return speechOutput;
}
function getSpeechCon(type) {
return "<say-as interpret-as='interjection'>" + speechConsWrong[getRandom(0, speechConsWrong.length-1)] + " </say-as><break strength='strong'/>";
}
function getRandom(min, max) {
return Math.floor(Math.random() * (max-min+1)+min);
}
exports.handler = (event, context) => {
const alexa = Alexa.handler(event, context);
//alexa.appId = APP_ID;
alexa.registerHandlers(handlers, startHandlers, gameHandlers);
alexa.execute();
};
You should look for the SessionEndedRequest when the session ends, so you can listen for that and respond accordingly. It should be request.type == 'SessionEndedRequest'. Much like LaunchRequest, it is not actually an intent, and I see you are already handling LaunchRequest, so it should be easy to add.

Is there a way to make "onkeydown" return slower

I want to increment an integer by holding down the right arrow key. The function i made works, but it returns too fast.
document.onkeydown = function (e) {
e = e || window.event;
if (e.keyCode == '39') {
var steps = localStorage.getItem("steps");
if (+steps < 9) {
if (+steps === +steps) {
localStorage.setItem("steps", +steps + +1)
}
} else {
localStorage.setItem("steps", +steps - +10);
}
var sss = localStorage.getItem("steps");
unicorn.className = "unicorn_" + sss + "";
return false;
}
}
The code above is where i'm at now. I am using localStorage to check the stored integer, and incrementing if it matches. Once the integer gets to 9, it substracts back to 0.
can anyone see what i'm doing wrong, or not doing right?
You can also manually keep track of the time using a closure:
document.onkeydown = (function () {
var T0 = Date.now();
return function (event) {
if (Date.now() - T0 > 500) {
console.log("doing my thing over here", Math.random());
T0 = Date.now();
}
}
})();
If you don't want it to execute too fast then consider placing it in a setTimeout
var notTooFast = false;
var timeout = 1000; // change it whatever you want to be
document.onkeydown = function (e) {
e = e || window.event;
if (e.keyCode == '39') {
if (!notTooFast)
{
var steps = localStorage.getItem("steps");
if (+steps < 9) {
if (+steps === +steps) {
localStorage.setItem("steps", +steps + +1)
}
} else {
localStorage.setItem("steps", +steps - +10);
}
var sss = localStorage.getItem("steps");
unicorn.className = "unicorn_" + sss + "";
notTooFast = true;
setTimeout(function () {
notTooFast = false;
}, timeout);
return false;
}
}
}

Stop function from re- executing for one second with settimeout

I want to prevent my function from re-executing for one second after it's last executed. I've tried the method below, but it doesn't work.
function displayOut() {
// images
document.getElementById("imgBox").style.backgroundImage = "url(" + db.rooms[roomLoc].roomImg + ")";
// Diologue box
diologueBox.innerHTML = ""; // Clear Box
teleTyperDiologue(db.rooms[roomLoc].description +
" The room contains: " +
(function() {
let x = "";
for (let i = 0; i < db.items.length; i++) {
if (db.items[i].location === roomLoc && db.items[i].hidden === false) {
x += db.items[i].name + ", "
}
}
x = x.slice(0, x.length -2);
if (x === "") {
x = " nothing of special interest";
}
return x;
})()
+ ".");
pause();
};
function pause() {
setTimeout(function() {
// Wait one second!
}, 1000);
}
You could use a pattern like this:
var executing = false;
function myFunc() {
if(!executing) {
executing = true;
//Code
console.log('Executed!');
//End code
setTimeout(function() {
executing = false;
}, 1000);
}
}
setInterval(myFunc, 100);
So in your case, this would look like this:
var executing = false;
function displayOut() {
if(!executing) {
executing = true;
// images
document.getElementById("imgBox").style.backgroundImage = "url(" + db.rooms[roomLoc].roomImg + ")";
// Diologue box
diologueBox.innerHTML = ""; // Clear Box
teleTyperDiologue(db.rooms[roomLoc].description +
" The room contains: " +
(function() {
let x = "";
for (let i = 0; i < db.items.length; i++) {
if (db.items[i].location === roomLoc && db.items[i].hidden === false) {
x += db.items[i].name + ", "
}
}
x = x.slice(0, x.length -2);
if (x === "") {
x = " nothing of special interest";
}
return x;
})()
+ ".");
setTimeout(function() {
executing = false;
}, 1000);
}
};
Try to use throttle (http://underscorejs.org/#throttle) or debounce (http://underscorejs.org/#debounce) from underscore, one of those should fit your needs
This one will achieve that:
function run () {
console.log('Im running');
pause(1000);
};
function pause(s) {
console.log('Im paused');
setTimeout(() =>{
run();
}, s)
};
run();
The code above will run every 1 sec but if you want to make sure the function cant be runned again until you decide then you could use a flag instead like:
let canExecute = true;
function run () {
if (canExecute) {
console.log('Im running');
canExecute = false;
pause(1000);
}
};
function pause(s) {
console.log('Im paused');
setTimeout(() =>{
canExecute = true;
}, s)
};
run();
run();
run();
setTimeout(() =>{
run();
}, 2000)
This code will execute run function twice, first on time and then one more after 2 sec.

how to count the number of buttons to change?

please help to fix the script or tell me which direction to think.
There is a calculator for ordering goods:
$(function(){
var price = {
'130': {
'list': {
'a3': {
'4+4': {
'500': 3255,
'1000': 3720,
'2000': 4935,
'3000': 5935,
'4000': 6935,
},
'4+0': {
'500': 3031,
'1000': 3456,
'2000': 4410,
},
},
'a4': {
}
},
'flaer': {
}
},
'160': {
}
};
var dict = {
'130': '130 г/м²',
'160': '160 г/м²',
'list': 'Листовки',
'flaer': 'Флаеры',
}
var str = '',
multi;
// Selecting something by clicking a button
$(document).on('click', '#calc button', function(event){
//event.preventDefault();
$(this).parent().data('selected', $(this).data('id'));
$(this).addClass('selected').siblings().removeClass('selected');
$('#calc_result').empty();
var priceitem = price,
param;
for (var i=0; i < 5; i++)
{
param = $('#calc div:eq('+i+')').data('selected');
if (param)
priceitem = priceitem[param];
else
break;
}
if (i == 5)
{
$('#calc_result').text(priceitem);
q = $('button.selected');
q.each(function(){
str = str + $(this).text() + ', ';
})
str = str.substring(0, str.length - 2);
$('#calc_summary').append(str);
}
// shadow hide
$(this).closest('.cell').next().find('.cell_shadow').css('display', 'none');
});
// Pre-filling
var priceitem = price;
for (var lvl=0; lvl < 5; lvl++)
{
var iter = 0;
for (var i in priceitem)
{
if (!iter)
priceitem = priceitem[i]
iter++;
$('#calc .lvl'+lvl).append('<button data-id="'+i+'">'+((i in dict) ? dict[i] : i)+'</button>');
}
}
// multiplication request
$('#odin_plus').on('click', function(){
if($('#calc_multi').html() == ''){
multi = 1;
addMulti(multi);
}
else{
multi = multi + 1;
addMulti(multi);
};
$('#odin_minus').css('visibility', 'visible');
});
$('#odin_minus').on('click', function(){
if($('#calc_multi').html().substring(2) != '1'){
multi = multi - 1;
addMulti(multi);
}
else{
$('#odin_minus').css('visibility', 'hidden');
$('#calc_multi').html('');
};
});
function addMulti(multi){
$('#calc_multi').html(' x' + multi);
};
$('#listovki_submit').on('click', function(){
alert('Ваша заявка отправлена ' + '\n' + str + ' ' + $('#calc_multi').text());
});
});
the problem is that when choosing the next block. cell_13 contains 5 buttons.
choosing the next block. cell_13 also contains 5 buttons (but should contain three buttons!)
I need to for the case of "4 + 0" block. cell_13 contained 3 buttons

How to shorten a jQuery function?

I have this jQuery function that work. Every 2 lines is the same except a minor changes. How can I shorten it?
$(".c1").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c2").fadeIn("slow", function() {
$(".c2").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c3").fadeIn("slow", function() {
$(".c3").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c4").fadeIn("slow", function() {
$(".c4").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c5").fadeIn("slow", function() {
$(".c5").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c6").fadeIn("slow", function() {
$(".c6").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c7").fadeIn("slow", function() {
$(".c7").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c8").fadeIn("slow", function() {
$(".c8").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c9").fadeIn("slow", function() {
$(".c9").delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c1").fadeIn("slow");
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
You could use a recursive function like this:
function phoneCall(i){
$(".c" + i).delay(5000).fadeOut("slow", function() {
$("#phone").addClass("c" + (i + 1)).fadeIn("slow", function() {
if(i <= 9) phoneCall(i + 1);
});
});
}
phoneCall(1);
It seems that the #phone element is the only one that ever gets the c_ class. If so, you can cache the element and eliminate a bunch of code.
var phone = $("#phone"), i = 0;
(function cycle() {
i = ((i % 9) + 1);
phone.addClass("c" + i).fadeIn("slow").delay(5000).fadeOut("slow", cycle);
})();
We can even get rid of a line of code by inlining the increment.
var phone = $("#phone"), i = 0;
(function cycle() {
phone.addClass("c" + ((++i % 9) + 1)).fadeIn("slow").delay(5000).fadeOut("slow", cycle);
})();
As #charlietfl noted, you may not want it to infinitely loop. If not, add a return statement.
var phone = $("#phone"), i = 0;
(function cycle() {
if (i === 9) return;
phone.addClass("c" + ((++i % 9) + 1)).fadeIn("slow").delay(5000).fadeOut("slow", cycle);
})();
And FWIW, numbering is usually a little simpler if you use 0 based indices.
var phone = $("#phone"), i = -1;
(function cycle() {
phone.addClass("c" + (++i % 9)).fadeIn("slow").delay(5000).fadeOut("slow", cycle);
})();
You can use something like that:
function inception(fromInt, tillInt){
if (fromInt < tillInt){
$('.c' + fromInt).delay(5000).fadeOut("slow",function(){
newInt = fromInt +1;
$('#phone').addClass('c'+newInt).fadeIn("slow", function() {
inception(newInt, tillInt));
}
});
}else{
if(fromint == tillInt){
$('.c' + fromInt).delay(5000).fadeOut("slow");
}
}
}
Then add to your code:
inception(1,9);
I don't know something like this?
var num = 2;
var HandlerForC = function () {
if (num < 10) {
$("#phone").addClass("c" + num).fadeIn("slow", HandlerForPhone);
} else {
$("#phone").addClass("c1").fadeIn("slow");
}
}
var HandlerForPhone = function () {
num++;
$(".c" + (num - 1)).delay(5000).fadeOut("slow", HandlerForC);
}
HandlerForPhone();

Categories