Currency Exchange - How to call a function within a function from onclick - javascript

Stackers: I coded up a foreign exchange calculator (Indian Rupees to US Dollars) with additions for the Indian counting system of crore and lakh. It works well enough as you see here: [Foreign Exchange Calculator][1] .
In order to improve it, I want to figure a way to have the code go out and grab a JSON object with today's exchange rate which is available via API in many places. While I can write a function that gets the exchange rate, I am having no luck getting an onclick call to make it work.
I looked at the jQuery page but couldn't find an answer to this. If I use the load function, the format is this:
$('selector').load('url',callback) {
do some callback of some kind
};
With a simple page, I can get this to work each time, but -
Because I want to trigger the call for the latest exchange rate with an onclick html call, I want to use a function from the onclick. My first question is, what should my selector be? Normally I would direct it to innerHTML via an id or a class but this doesn't seem to work here.
Secondly, it would seem that I should set my code so that when the page loads, it goes out and notes the current exchange rate. Is there a way for JavaScript/jQuery to do this upon page load even if I haven't called on the function yet? Optimally, I would set the variable to today's rate once and save it locally so I only need to update once a day.
Finally, I have set the function call related to the onclick as:
onclick = run(123.45) with 123.45 being today's rate.
is there a way to replace 123.45 with a function, so a function within a function? I want to use today's rate as returned from the jQuery .load method as the argument to populate the run function call. No matter how I try, I can't get it to work. I have searched google and haven't found anything which leads me to believe that I am going about this all wrong.
Thanks for any assistance.
// js logic to run calculator with the assumption that each
// calculation involves some pos or negative number and then
// an operator (+-/*) which always yields a positive number.
// ex: 2 + 2 = (4 elements) adding another number and operator
// ex: / 3.5 is now 6 elements in the array.
arr = [];
var total = 0, rupee;
var box = document.getElementById("display");
//jquery call for current Rupee to USD exhcange rate
var url = "https://api.fixer.io/latest?base=USD";
// BELOW IS THE CODE FROM MY FIRST QUESTION
$(**WHATSHOULDTHISSELECTORBE?**).load(url, function(responseTxt) {
var obj = JSON.parse(responseTxt);
rupee = obj.rates.INR;
});
// end of rupee to dollar call
function run(digit) {
x = box.value;
if (x !== '.' && isNaN(x)) {
box.value = "";
}
box.value += digit;
}
function runPlus() {
var digits = box.value;
if (digits === "") {
return;
}
arr.push(Number(digits)); // tried parseInt but dropped decimal nos.
box.value += "+";
arr.push("+");
console.log(arr);
}
function runMinus() {
var digits = box.value;
if (digits === '') {
arr.push("-");
return;
}
arr.push(Number(digits));
box.value = "";
box.value = "-";
arr.push("-");
console.log(arr);
}
function runMult() {
var digits = box.value;
if (digits === "") {
return;
}
arr.push(Number(digits));
box.value = "";
box.value += "*";
arr.push("*");
console.log(arr);
}
function runDiv() {
var digits = box.value;
if (digits === "") {
return;
}
arr.push(Number(digits));
box.value = "";
box.value += "/";
arr.push("/");
console.log(arr);
}
function runEquals() {
var digits = box.value;
if (digits === "") {
return;
}
arr.push(Number(digits));
arr.push("=");
box.value = "";
total = arr[0];
var ans = calculateArr(arr);
box.value = ans;
}
function runClear() {
box.value = "";
arr = [];
}
function calculateArr(arr) {
for (var i = 0; i < arr.length; i = i + 2) {
if (arr[i + 1] === "+") {
total = total + arr[i + 2];
} else if (arr[i + 1] === "-") {
total = total - arr[i + 2];
} else if (arr[i + 1] === "*") {
total = total * arr[i + 2];
} else if (arr[i + 1] === "/") {
total = total / arr[i + 2];
} else if (arr[i + 1] === "=") {
total = total;
} else alert("Error");
}
return ReplaceNumberWithCommas(total);
}
function ReplaceNumberWithCommas(yourNumber) {
//Seperates the components of the number
var n = yourNumber.toString().split(".");
//Comma-fies the first part
n[0] = n[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
//Combines the two sections
if (n[1]) {
n[1] = n[1].split("").slice(0, 2);
n[1] = n[1].join("");
}
return n.join(".");
}
[1]: http://codepen.io/Qstreet/pen/GovZdg

Since you need to retrieve values automatically on page load , you need to use xhr request.
$.get( "your-url", function( data ) {
$( "where you want to insert it" ).html( data );
//or call your-custom-function(data);
});
If you want to fetch values upon click event , insert above code as a function to jquery click event handler

Thanks so much #Anmol. I got it working with this:
var url = "http://api.fixer.io/latest?base=USD";
function runRup() {
var xhr = new XMLHttpRequest();
xhr.open("GET",url,false);
xhr.send(null);
var rt = JSON.parse(xhr.responseText);
return rt.rates.INR;
};

Related

Having trouble counting iterations of different outputs in my javascript code

Trying to set up code to take inputted numbers, determine whether they are negative or positive, displaying this, and then when 0 is put in the code displays the amount of positive numbers put in and the amount of negative numbers put in and then terminates. Having trouble with the counting part and I am not sure how to set this up better. I am also not sure how to set this up to terminate after 0 is put in.
function mapping() {
var num = parseInt(document.getElementById("num1").value);
var countp = 0;
var countn = 0;
if (num !== 0) {
if (num > 0) {
document.getElementById("output").innerHTML = "positive";
countp += 1;
} else {
document.getElementById("output").innerHTML = "negative";
countn += 1;
}
} else {
document.getElementById("output").innerHTML = "countp: " + countp;
document.getElementById("output").innerHTML = "countn: " + countn;
}
}
Thank you.
Two problems with the code.
1st: You need to move countp and countn outside of the function to make them global.
2nd: You are writing the positive number counts to output's html and then you are overriding it by negative count.
This should do the trick;
var countp = 0;
var countn = 0;
function mapping() {
var num = parseInt(document.getElementById("num1").value);
if (num !== 0) {
if (num > 0) {
document.getElementById("output").innerHTML = "positive";
countp += 1;
} else {
document.getElementById("output").innerHTML = "negative";
countn += 1;
}
} else {
var html = "countp: " + countp + ", countn: " + countn;
document.getElementById("output").innerHTML = html;
// this line added
countp = 0, countn = 0;
}
}
<input type="text" id="num1">
<button onclick="mapping()">Test</button>
<div id="output">
</div>
The main issue with the code is that countp and countn are local variables. Thus they are created and initialized to 0 every time the function is called. Move the two lines outside the function to fix this bug!
Another bug is the code in the last else part. There you set innerHTML twice, so the div ends up with only countn. To fix this bug, replace the last innerHTML = by innerHTML +=.
Finally, if I understand you correctly, you want that no more updates occur once 0 has been entered. To achieve this, you could add another variable like isFinal that is set to true when the user enters 0, and add a check to your function.
Some more suggestions:
Instead of if (num!==0), it is considered good practice to start with positive conditions such as if (num === 0). That way, you will also avoid some nesting in the conditions.
What happens if the user does not enter a valid number? In your code, this will be treated as negative number. Add a test for "NaN" to fix this.
You repeat the document.getElementById... many times. Use a temporary variable to fix this.
In modern JavaScript, it is recommended to use let or const instead of var.
Be consistent in your use of semicolons at the end of lines.
Thus the code ends up as:
let countp = 0;
let countn = 0;
let isFinal = false;
function mapping() {
if (isFinal) {
return;
}
const num = parseInt(document.getElementById("num1").value);
let html = "";
if (Number.isNaN(num)) {
html = "invalid";
} else if (num === 0) {
html = "countp: " + countp + "<br>";
html += "countn: " + countn;
isFinal = true;
} else if (num > 0) {
html = "positive";
countp += 1;
} else {
html = "negative";
countn += 1;
}
document.getElementById("output").innerHTML = html;
}

JavaScript Dynamically created object undefined

I am doing the freecodecamp algorithmic challenge "Caesars Cipher". I have a problem with my code. I try to generate a lookup table as a dynamic object and for some reason it doesn't register. When doing console.log it is says "lookup table is undefined". It is the same with the Acode variable. If I comment out the console.logs then it will work but it will not encrypt anything because of the below part which checks if the char from strArr exists in the lookupTable, if not, it should assign the same value to the encryptedArr (this was done to not encrypt commas, spaces etc):
strArr.forEach(function(thisArg) {
var newValue;
if(lookupTable[thisArg] !== undefined ) {
newValue = lookupTable[thisArg];
} else {
newValue = thisArg;
}
encryptedArr.push(newValue);
});
Ofcourse lookupTable[thisArg] is always undefined.
Here is the whole function with the above part as well:
function rot13(str) { // LBH QVQ VG!
var strArr;
var encryptedArr = [];
var Acode;
var lookupTable = {}; //this object will contain the mapping of letters
var encryptedString;
//check the code of A , this will be a reference for the first letter as the algorith will use Modular Arithmetic
Acode = 'A'.charCodeAt(0);
console.log(Acode);
//generate an object containing mappings (I din't want to do it initially but theoreticaly just making lookups in a table would be more efficiant for huge workloads than calculating it every time)
//this algorithm is a little bit complecated but i don't know how to do modular arithmetic in code properly so I use workarrounds. If a = 101 then I do 101 + the remainder from current letter((Acode + 1) - 13) divided by 26 which works
for (i = 0; i < 26; i++) {
lookupTable[String.fromCharCode(Acode + i)] = String.fromCharCode(Acode + ((Acode + i) - 13) % 26);
console.log(lookupTable[String.fromCharCode(Acode + i)]);
}
//save the string into the array
strArr = str.split("");
//change letters into numbers and save into the code array
strArr.forEach(function(thisArg) {
var newValue;
if (lookupTable[thisArg] !== undefined) {
newValue = lookupTable[thisArg];
} else {
newValue = thisArg;
}
encryptedArr.push(newValue);
});
encryptedString = encryptedArr.join("");
return encryptedString;
}
// Change the inputs below to test
rot13("SERR PBQR PNZC");
console.log(Acode);
What am I doing wrong with the lookupTable object creation AND with the below?
Acode = 'A'.charCodeAt(0);
There's no undefined variable. The problem with your code is in how you calculate the lookup table entries. Your code is mapping every character to itself, not shifting by 13. The correct formula is
Acode + ((i + 13) % 26)
Acode is the ASCII code for the letter, and you shouldn't be including that when performing the modular shift. You just want to apply the modulus to the offset from the beginning of the alphabet after shifting it by 13.
function rot13(str) { // LBH QVQ VG!
var strArr;
var encryptedArr = [];
var Acode;
var lookupTable = {}; //this object will contain the mapping of letters
var encryptedString;
//check the code of A , this will be a reference for the first letter as the algorith will use Modular Arithmetic
Acode = 'A'.charCodeAt(0);
// console.log(Acode);
//generate an object containing mappings (I din't want to do it initially but theoreticaly just making lookups in a table would be more efficiant for huge workloads than calculating it every time)
//this algorithm is a little bit complecated but i don't know how to do modular arithmetic in code properly so I use workarrounds. If a = 101 then I do 101 + the remainder from current letter((Acode + 1) - 13) divided by 26 which works
for (i = 0; i < 26; i++) {
lookupTable[String.fromCharCode(Acode + i)] = String.fromCharCode(Acode + ((i + 13) % 26));
// console.log(lookupTable[String.fromCharCode(Acode + i)]);
}
//save the string into the array
strArr = str.split("");
//change letters into numbers and save into the code array
strArr.forEach(function(thisArg) {
var newValue;
if (lookupTable[thisArg] !== undefined) {
newValue = lookupTable[thisArg];
} else {
newValue = thisArg;
}
encryptedArr.push(newValue);
});
encryptedString = encryptedArr.join("");
return encryptedString;
}
// Change the inputs below to test
var result = rot13("SERR PBQR PNZC");
console.log(result);

How to pass an array through Javascript Function Arguments

I have found a couple other similar threads on stackoverflow (Pass Array Thread 2) and (Pass Array Thread 1) as well as from a few other sites but I either did not understand them, they did not fully answer my question, or I did not know how to implement it into my code.
I have the following code which should create a map for a game based on some arrays:
function createMap(level) {
var map = document.getElementById('map');
mapWidth = parseInt(level[0]);
mapHeight = parseInt(level[1]);
map.innerHTML = '';
rowNumber = 1;
tileID = 1;
var consoleHelp = level[7];
console.log(k+' and value is '+consoleHelp);
k = 1;
for (k = 1; k <= mapHeight; k++) { // repeat below loop until specified height is reached
for (k = 1; k <= mapWidth; k++) { // create a row of tiles based on the specified width of the array
console.log('Row '+k+' created')
if (rowNumber == 1) {
k++;
}
else {
k--;
}
if (level[k] == 'w') {
map.innerHTML += '<span id="'+rowNumber+'-'+tileID+'">desert<image class="tiles" src="desert.png"></span>';
}
else if (level[k] == 'g') {
map.innerHTML += '<span id="'+rowNumber+'-'+tileID+'"><image class="tiles" src="grass.png"></span>';
}
else {
console.log('crap dis did not work');
var consoleHelp = level[k];
console.log(k+' and value is '+consoleHelp);
}
if (rowNumber == 1) {
k--;
}
else {
k++;
}
tileID++
}
rowNumber++
level = level + '_1';
map.innerHTML = "<br>";
}
spawnTile();
}
and the variable arrays (incomplete but you get the idea):
var map_beginning_1 = ['20','10','w','w','w','w','w','w','w','w','w','w','w','w','w','w','w','w','w','w','w','w'];
var map_beginning_1_1 = ['w','g','g','g','g','g','g','g','g','g','g','g','g','g','g','g','g','g','g','w'];
My problem is that I call in
createMap('map_beginning_1')
and nothing happens. There's no errors but nothing else happens. I did a test to see what it was getting and the value of "level[7]" is "i" and "level1" is "a", which is the location of the characters in "map_beginning_1". Can someone please explain to me how to or if it's even possible to variably pass an array through a Javascript function argument and how to do it? And if it isn't possible, can you suggest any alternatives for what I'm trying to do? Javascript is preferable but I can use Jquery if I must.
You have passed a string into the function and not a variable please try the following, removing the single quotes.
createMap(map_beginning_1);
Try createMap(map_beginning_1). Lose the ' (quotes), as you are trying to pass an array but are actually passing a string.

Textarea input length check in JavaScript

I'd like to create an SMS gateway which alerts the user each time when 160 chars are written (or pasted). I need to store a variable n containing number of message parts of 160 chars. The code will be like this, just the n needs to be stored globally. Is there any better way than e. g. storing it into a hidden form field?
Note: an assumption that less than 160 chars will be pasted at once is safe in this particular case.
window.onload = function() {
var n=1;
var t=document.getElementById('msg');
t.addEventListener('input', function() {
var l=t.value.length;
if(l>n*160){
n++;
alert('Message will be split into '+n+' parts');
}
}, false);
}
As mentioned in my comment, it depends on what you want to do with n ultimately.
If you just want it to show the user a message, you can update the DOM once you've calculated this value.
Your current example doesn't allow the user to delete text after they've typed/pasted it in. A better example would be stateless, i.e. don't compare n to itself, as in my example below:
var t = document.getElementById('msg');
var splitSize = 10;
var n = 1;
t.addEventListener('input', function() {
var nextN = 1;
if (t.value.length > 0) {
nextN = Math.ceil(t.value.length / splitSize);
}
if (n !== nextN) {
n = nextN;
var label = document.getElementById('label');
if (n === 1) {
label.innerHTML = '';
} else {
label.innerHTML = 'The message will be split into ' + n + ' parts';
}
}
});
<p id="label"></p>
<textarea id="msg"></textarea>

Modifying Luhn checker function to check for Laser cards

I use the following script to validate the card details entered in a form. I would like to add a function to this so that a visitor using a Laser card is alerted that we don't accept them.
The start digits of the Laser are 6304, 6706, 6771 & 6709
function Calculate(Luhn)
{
var sum = 0;
for (i=0; i<Luhn.length; i++ )
{
sum += parseInt(Luhn.substring(i,i+1));
}
var delta = new Array (0,1,2,3,4,-4,-3,-2,-1,0);
for (i=Luhn.length-1; i>=0; i-=2 )
{
var deltaIndex = parseInt(Luhn.substring(i,i+1));
var deltaValue = delta[deltaIndex];
sum += deltaValue;
}
var mod10 = sum % 10;
mod10 = 10 - mod10;
if (mod10==10)
{
mod10=0;
}
return mod10;
}
function Validate(Luhn)
{
var LuhnDigit = parseInt(Luhn.substring(Luhn.length-1,Luhn.length));
var LuhnLess = Luhn.substring(0,Luhn.length-1);
if (Calculate(LuhnLess)==parseInt(LuhnDigit))
{
return true;
}
alert("\n\nError with your card number! \nPlease check and correct.\n\n")
return false;
I wouldn't modify these functions to check the card type - they have a specific goal. If you change the Validate(Luhn) function so that it fails a card that passes its Luhn check but that has specific starting digits, you're making trouble for yourself later. Instead, add a new function, something like this:
function checkCardType(CardNumber) {
var CardStart = CardNumber.substring(0, 4);
if ((CardStart == '6304') || (CardStart == '6706') || (CardStart == '6771') || (CardStart == '6709')) {
alert('We do not accept Laser cards.');
return false;
} else {
return true;
}
}
Could you just keep a blacklist? You'd need to have a process to keep it up-to-date (e.g. load it from a database, manage it there - keeping it in the source is more hassle in the long term), but for such a small number of items, it would be useful:
function LoadBlacklist() {
// this is the simplest example; you may want to load the array dynamically
var bad_prefixes = [ "6304", "6706", "6771", "6709" ];
return bad_prefixes;
}
function Validate(Luhn)
{
var blacklist = LoadBlacklist();
var luhn_prefix = Luhn.substring(0,4); // only check the first 4 characters
for (var bl_pos = blacklist.length - 1; bl_pos >= 0; bl_pos--) {
if (blacklist[bl_pos] == luhn_prefix) {
// this card matches one of the blacklist prefixes
return false;
}
}
// if we're here, no match was found
// go on with validation...
}

Categories

Resources