I have a fairly long javascript file below, but it's not important to understand the entire file, only the 'totalItemCount' variable and how it is updated. This code uses the Igloo API to recursively count the number of files within a directory (and that count is stored in the totalItemCount var). I have never used recursion with Javascript before, so I may have made a mistake there. For some reason the count increases and then goes down later on in the javascript console, which makes me think I have made a mistake updating that variable somewhere (and it's only updated in a few places but I can't find it)
EDIT: I now have it updating more accurately, but items are over-counted or under-counted by about 10%. I think the last few updates might be where the problem is but i'm not sure. The code below is updated:
new ApiClient({
apimethod: 'objects/7f6d706e-6754-e411-b5b8-d4ae5294c399/children/view',
method: 'get',
queryparams: {
maxcount: 8000,
startindex: 0,
includefuturepublished: true
},
onSuccess: function (responseText)
{
var result = JSON.parse(responseText);
var originalObject = result; //first, top-level object
var totalItemCount = 0; //UPDATED HERE
console.log(result.response);
console.log(result.response.items);
console.log('RESPONSE TITLE: ' + result.response.items[0].title);
console.log("\n\n\n");
totalItemCount += parseInt(result.response.totalCount); //UPDATED HERE
console.log("totalItemCount: " + totalItemCount);
//Check if object has children and add to totalItemCount accordingly FOR EACH object:
function getItemsRecursively(totalItemCount1)
{
for(var i = 0; i < parseInt(totalItemCount); i++) //at this point, totalCount == #objects at this lvl
{
console.log("FOR LOOP TEST: " + i);
var currentObject = result.response.items[i];
console.log("href/dir: " + currentObject.href);
console.log("title: " + currentObject.title);
console.log("numchildren: " + currentObject.numchildren);
if(currentObject.numchildren > 0 && currentObject.numchildren != undefined)
{
console.log("it has children...");
getChildrenItemCount(totalItemCount1, currentObject);
}
console.log("New totalItemCount: " + totalItemCount);
console.log("\n~~~~~ NEXT OBJECT ~~~~~\n");
}
}
function getChildrenItemCount(totalItemCount2, previousObject)
{
//totalItemCount2 = totalItemCount;
var childID = previousObject.id;
console.log("childID: " + childID);
new ApiClient
({
apimethod: 'objects/' + childID + '/children/view',
method: 'get',
queryparams: {
maxcount: 8000,
startindex: 0,
includefuturepublished: true
},
onSuccess: function (responseText)
{
console.log("getChildrenItemCount successful...");
var result = JSON.parse(responseText);
console.log(result);
var currentObject = result.response;
var currentFolderItemCount = currentObject.totalCount;
console.log("currentFolderItemCount: " + currentFolderItemCount);
for(var i = 0; i < parseInt(currentFolderItemCount); i++) //at this point, totalCount == #objects at this lvl
{
console.log("CHILDREN FOR LOOP TEST: " + i);
var currentObject = result.response.items[i];
console.log("href/dir: " + currentObject.href);
console.log("title: " + currentObject.title);
console.log("numchildren: " + currentObject.numchildren);
if(currentObject.numchildren > 0 && currentObject.numchildren != undefined)
{
console.log("it's children has children...");
totalItemCount += parseInt(currentObject.numchildren); //UPDATED HERE
totalItemCount2 = totalItemCount; //UPDATED HERE
console.log("totalItemCount after one sub-child total: " + totalItemCount);
getChildrenItemCount(totalItemCount2, currentObject);
}
console.log("New totalItemCount after ENTIRE getChildrenItemCount: " + totalItemCount);
console.log("\n~~~~~ NEXT OBJECT WITHIN CHILDREN ~~~~~\n");
console.log("\n\n\n\nFINAL ITEM COUNT: " + totalItemCount);
}
}
})
//return totalItemCount;
}
getItemsRecursively(totalItemCount);
console.log("\n\n\n\nFINAL ITEM COUNT: " + totalItemCount);
}
});
It's a "scope" problem, you're using the same variable name as parameter of function and out the function, so the parameter overwrite the global variable.
See following please:
var global = 10;
function test(global){
global += 100;
console.log(global);
}
console.log(global);
test(global);
console.log(global);
You simply have to remove the var function's parameters.
var global = 10;
function test(){
global += 100;
console.log(global);
}
console.log(global);
test(global);
console.log(global);
I hope it was clear, bye.
Related
I have a client-side web-application that takes a csv-file, parses it into various data types, searches for something specific, and displays a table with the answer on the screen. The search function returning a null string. This occurs because its search parameter, returned by a callback function and put into lib, returns null.
I'm fairly certain this is a callback issue, but I've messed around with the order so much I'm not sure what goes where anymore in my html...A second set of eyes would be appreciated.
The desired series of events
fileToArray() gives us an array
search() looks in the array for its specified item and returns a csv-format string containing what it found
displayTable takes that csv-format string and outputs it to the desired location
The Code
// jQuery call to fetch the client-side csv file - this works when called by itself.
const fileToArray = () => {
console.log("fileToArray started.");
$.get({
url: CSV_LOCATION,
dataType: "text",
success: function (result) {
console.log("splitting result by newline...");
let csvLines = result.split("\n");
console.log("split successful. generating array into retval ...");
let retval = [];
for (let i = 0; i < csvLines.length; i++) {
// [0][0] is number [0][1] is class, [0][2] is unit, [0][3] is lesson
retval[i] = csvLines[i].split(",");
}
console.log("success! Returning retval.");
return retval;
// callback(result);
// return result;
},
failure: function (xhr, status, error) {
console.log("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
alert("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
}
})
};
// PRECONDITION: form is #search-params in index.js
// > lib is the result of fileToArray()
// POSTCONDITION: result is a csv-format string to be passed to displayTable() in index.js
const search = (form, callback) => {
console.log("search called...");
// vvvvv The probable root of the problem vvvvv //
let lib = callback;
console.log(lib.length + " is lib's length.");
let result = "";
console.log("search nested for loop called...");
for (let i = 0; i < lib.length; i++) {
// check class
console.log("checking class " + form.class.value + "...");
if (lib[i][1] === form.class.value) {
// check unit
console.log("checking unit " + form.unit.value + "...");
if (Number(lib[i][2]) === Number(form.unit.value)) {
console.log("adding to result...");
result += lib[i] + "\n";
}
}
}
console.log("search success! result: " + result.length + " characters");
console.log(result);
return result;
};
<!-- I'm almost 100% certain I've messed up the callback in this button,
but I still don't quite understand how... I've played with
displayTable(fileToArray(search(...))), but I don't quite know how it should go -->
<button class="btn btn-primary"
onclick="displayTable(search(document.getElementById('search-params'), fileToArray), $('#card-display'))">
Submit
</button>
What I've tried
I have looked to the following sites for inspiration (none have helped):
JavaScript is Sexy
JavaScript: Passing parameters to a callback function
JavaScript Callback Functions
Passing arguments to callback functions
In Summary
It's painfully obvious I still don't understand callbacks fully. Any help would be appreciated.
You could use async / await
const displayTable = async () => {
let arrayFromFile = await fileToArray(); // fileToArray executes and assigns the returned value when it completes
let searchedData = search(form, arrayFromFile);
// Display the table
};
Thanks to #kapantzak for the inspiration!! Turns out, I was using callbacks horribly bass-ackwards. According to this, the old-school async style is something akin to
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
So, the relevant code now looks like this:
const fileToArray = (callback) => {
// console.log("fileToArray started.");
$.get({
url: CSV_LOCATION,
dataType: "text",
success: function (result) {
let csvLines = result.split("\n");
let retVal = [];
for (let i = 0; i < csvLines.length; i++) {
// [0][0] is number [0][1] is class, [0][2] is unit, [0][3] is lesson
retVal[i] = csvLines[i].split(",");
}
callback(retVal);
},
failure: function (xhr, status, error) {
console.log("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
alert("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
}
})
};
// =======
const search = (form, lib, callback) => {
let result = "";
let formClass = form.class.value.toLowerCase();
let formUnit = form.unit.value.toLowerCase();
let formLesson = form.lesson.value.toLowerCase();
for (let i = 0; i < lib.length; i++) {
// check class
if (lib[i][1].toLowerCase() === formClass) {
// check unit
if (Number(lib[i][2].toLowerCase()) === Number(formUnit)) {
result += lib[i] + "\n";
}
}
}
console.log(result);
callback(result);
};
<button class="btn btn-primary"
onclick="fileToArray(function(result) {
search(document.getElementById('search-params'), result, function(newResult) {
displayTable(newResult, $('#card-display'));
});
});">
Submit
</button>
This righted the wrongs and caused my search and display to function properly.
I'm pretty new to JS so I'm struggling here. Basically I have a score function in place in my JavaScript and I want to store this locally so it can be retrieved by another page at a later time. I used local storage for my login and register page, but I'm not sure how I can do the same for score.
add_row_of_pipes: function() {
var hole = Math.floor(Math.random()*5)+1;
for (var i = 0; i < 8; i++)
if (i != hole && i != hole +1)
this.add_one_pipe(400, i*60+10);
this.score += 1;
this.label_score.content = this.score;
};
All feedback will be highly appreciated:)
This is how you can store and retrieve values from localStorage:
// Store
localStorage.setItem("score", "100");
// Retrieve
var score = localStorage.getItem("score");
This is a small LocalStorage framework that i've put together.
It covers all of the obvious functionality you would want from localstorage.
function set_LocalStorage(key,value)
{
//localStorage.setItem("name of variable", "value to store");
localStorage.setItem(key, value);
console.log('LocalStorage: ' + key + ' has been set to: ' + value);
}//End set_LocalStorage
function get_LocalStorage(key)
{
return localStorage.getItem(key);
console.log('LocalStorage: ' + key + ' has a value of: ' + value);
}//End get_LocalStorage
function remove_LocalStorage(key)
{
localStorage.removeItem(key);
console.log('LocalStorage: ' + key + ' has been removed');
}//End remove_LocalStorage
function check_LocalStorage_exist(key)
{
var v = get_LocalStorage(key);
var v2 = toInteger(v);
var FeedBack;
if(v2 == 'null' || v2 === 'NaN' || v2 == 'undefined' || v2 == ''){ /*console.log('key '+key+' does NOT exist');*/ FeedBack='NO';}
if(v2!=0){
console.log('key '+key+' exist');
FeedBack='YES';
}
return FeedBack;
}//End check_LocalStorage
function list_All_LocalStorage()
{
for (var i = 0; i < localStorage.length; i++)
{
let item = localStorage.getItem(localStorage.key(i)); //--Will only need to have this on when collecting APP_DATA
console.log('------ LocalStorage: '+localStorage.key(i)+' = '+item);
}
}//End list_All_LocalStorage
function remove_All_LocalStorage()
{
for (var i = 0; i < localStorage.length; i++)
{
let s_key = localStorage.key(i);
remove_LocalStorage(s_key);
}
}//End remove_All_LocalStorage
You're welcome! :)
I'm new to Javascript, and callbacks are blowing my mind a little at the moment. How do I turn the teletyperDiologue function into a callback? The main reason is I want the teletyper to finish it's job before the displayOut function finishes. Thank you in advance for your help.
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;
})()
+ ".");
};
// Teletyper for Diologue Box
function teleTyperDiologue(string) {
for (let i = 0; i < string.length; i++) {
setTimeout(function() {
diologueBox.innerHTML += string.slice(i, i + 1);
}, 5 * i);
}
}
As an example:
function test(a) { a(); }
function x() { alert('hello'); }
test(x);
in your case:
function displayOut(callback) {
// images
document.getElementById("imgBox").style.backgroundImage = "url(" + db.rooms[roomLoc].roomImg + ")";
// Diologue box
diologueBox.innerHTML = ""; // Clear Box
callback(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;
})()
+ ".");
};
displayOut(teleTyperDiologue);
You can pass functions around like variables and return them in functions and use them in other functions. So, when you pass a callback function as an argument to another function, you only need to pass the function definition.
See example below.
function displayOut() {
console.log("Display Out running...");
}
function teleTyperDiologue(stringParam, callback) {
console.log("Running teleTyper with string param passed of: ", stringParam);
callback();
}
teleTyperDiologue ("Test string", displayOut);
So i made this cracker/bruteforce just to learn / test nuthin illegal here since am doing it via localhost and my own php login.
So am keep getting Failed:undefined:undefined
When its suppose to be this Failed:user:pass
Any help?
Thanks
<script>
function crack() {
var cracked_area = document.getElementById("cracked_acc");
var failed_area = document.getElementById("failed_acc");
var num_users = 0;
var num_cracked = 0;
var html_next;
var user_l = document.getElementById("users");
var pass_l = document.getElementById("pass");
if (user_l.value == "") {
alert("The username field cant be empty!")
} else if (pass_l.value == "") {
alert("The password field cant be empty!")
}
var message = document.getElementById('message');
var user_l_s = user_l.value.split("\n");
var pass_l_s = pass_l.value.split("\n");
for (var i = 0; i < user_l_s.length; i++) {
num_users++;
for (var j = 0; j < pass_l_s.length; j++) {
$.ajax({
url: 'php/postdata.php',
type: 'GET',
data: {'user': user_l_s[i], 'pass': pass_l_s[j]},
dataType: 'text',
success: function (response) {
html_next = response;
if (html_next.indexOf("Failed") > -1) {
failed_area.value += 'Failed:' + user_l_s[i] + ':' + pass_l_s[j] + '\n';
} else if (html_next.indexOf("Cracked") > -1) {
num_cracked++;
cracked_area.value += 'Cracked:' + user_l_s[i] + ':' + pass_l_s[j] + '\n';
}
},
beforeSend: function () {
message.innerHTML = "Loading results...";
}
});
}
message.innerHTML = "Total Accounts Cracked:" + num_cracked + "/" + num_users;
}
}
In your success-callback you refer to the variable i.
But think about what happens when the for-loop runs. The loop is already finished when the callback is called and thats why i equals pass_l_s.length and therefore user_l_s[i] is undefined.
When you want to use the variable i in a callback you have to create your little own world with a local i
success: (function(i){
return function (response) {
html_next = response;
if (html_next.indexOf("Failed") > -1) {
failed_area.value += 'Failed:' + user_l_s[i] + ':' + pass_l_s[j] + '\n';
} else if (html_next.indexOf("Cracked") > -1) {
num_cracked++;
cracked_area.value += 'Cracked:' + user_l_s[i] + ':' + pass_l_s[j] + '\n';
}
}
})(i)
We defined a self-invoking function here which expects the parameter i. When the for-loop now loop through all i everytime a new function is returned from the self-invoking function which now has access to i cause its locally defined as parameter of the self-invoking function.
Good to know
Javascript1.7 introduces a new keyword let which serves for exactly that purpose. But at these days its not usable in production
That's because you call the ajax() function within the loop. It's asynchronous call made in a loop statement and by the time the response arrives back to the client the loop is already over and hence the user_l_s[i] will be undefined. Change the response format to return which user has failed to login.
I have a fiddle located here illustrating my problem, namely that when I load images using the Image object, onload gets called but the returned object is still undefined.
Here's the code in case the fiddle were to disappear into the void:
var ImageManager = function() {
var _images = [],
_loaded = 0;
var precache = function(imageArray) {
if (!(imageArray instanceof Array)) {
return;
}
for (var i = 0; i < imageArray.length; i++) {
if (typeof imageArray[i] !== 'string') {
continue;
}
_images[i] = new Image();
_images[i].onload = function() {
_loaded++;
document.write('<p>Successfully loaded: ' + imageArray[i] + ' [' + _loaded + '/' + imageArray.length + ']</p>');
}
_images[i].onerror = function() {
document.write('<p>Unable to load: ' + imageArray[i] + '</p>');
}
_images[i].src = imageArray[i];
}
};
var get = function(imagePath) {
if (!_images.hasOwnProperty(imagePath)) {
document.writeln('<p>' + imagePath + ' isn\'t precached!</p>');
return null;
}
return _images[imagePath];
};
return { get: get, precache: precache }
};
// Test case starts here:
var testImages = [
'http://jsfiddle.net/img/logo.png',
'http://jsfiddle.net/img/logo.png',
'http://jsfiddle.net/img/logo.png'
];
var imageManager = new ImageManager();
imageManager.precache(testImages);
Sample output:
Successfully loaded: undefined [1/3]
Successfully loaded: undefined [2/3]
Successfully loaded: undefined [3/3]
As per usual, it's likely something obvious I'm overlooking. Aid me, ye all-knowing oracles.
i inside the event handlers are always equal to imageArray.length - 1, because i increases during the loop. Wrap the loop's body in a closure:
Also, don't use document.write, even for debugging purposes. Use console.log(..), alert(..) or at least document.body.innerHTML += ...
Demo: http://jsfiddle.net/dpDXp/7/
for (var i = 0; i < imageArray.length; i++) {
if (typeof imageArray[i] !== 'string') {
continue;
}
(function(i){ //<-- This
_images[i] = new Image();
_images[i].onload = function() {
_loaded++;
console.log('Successfully loaded: ' + imageArray[i] + ' [' + _loaded + '/' + imageArray.length + ']');
}
_images[i].onerror = function() {
console.log('Unable to load: ' + imageArray[i]);
}
_images[i].src = imageArray[i];
})(i); //<-- This
}