I am trying to complete the twitch api at freecodecamp.
I have an allTotalUsers array with the users I am looping through.
My loop appears to grabbing the info I need for each user in my allTotalUsers list correctly.
I tested this by displaying data from the offline users and it showed(multiple).
The issue is on my if(streamInfo==null) statement, the streamerName is showing duplicates only.
For example, If there is 2 offline users, Bob and John.
It will show
Bob
Bob
function showOfflineUsers() {
var onlinePeople = $('#displayInfo');
onlinePeople.html('');
//allTotalUsers is an array list that is loaded when the page loads and stored globally.
for (var i = 0; i < allTotalUsers.length; i++) {
var streamerName = allTotalUsers[i];
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status == 200) {
var json = JSON.parse(this.responseText);
// gets the status of stream(null etc)
var streamInfo = json['stream'];
if (streamInfo == null) {
onlinePeople.append("<li>" + "<a href = 'https://www.twitch.tv/" + streamerName + "'>" + streamerName + "</a>" + "</li>" + "<br>");
}
}
}
xmlhttp.open("GET", "https://api.twitch.tv/kraken/streams/" + streamerName + "?client_id=" + clientId, true);
xmlhttp.send();
};
}
As per spec
let and const declarations define variables that are scoped to the
running execution context’s LexicalEnvironment. The variables are
created when their containing Lexical Environment is instantiated but
may not be accessed in any way until the variable’s LexicalBinding is
evaluated.
So, as opposed to var which is scoped to VariableEnvironment (of execution's context), let is scoped to LexicalEnvironment.
Replace
var streamerName = allTotalUsers[i];
with
let streamerName = allTotalUsers[i];
Related
I am building a simple open source Chromium extension that retrieve some data from several urls and then update the DOM. I could find another way to do this than by adding the line to update the DOM inside the callback http1.onreadystatechange
My XMLHttpRequest requests were often stuck on http1.readyState = 3 so I have added a 3rd parameter to http1.open("GET"); to make the request synchronous like this:
http1.open("GET", url, false);
But I am still getting these errors:
results[1].join is not a function at XMLHttpRequest.http.onreadystatechange
annot read property 'join' of undefined at XMLHttpRequest.http.onreadystatechange
Even thought they don't prevent the script from running, I think this isn't the right way to do what I want. So here is my question: how to update the DOM with the responses from several XMLHttpRequest request? Let's say I need to retrieve and compare all the data before updating the DOM. Then is there a way to process all the data at once after we have retrieve all of them (cf my comment on the last line)?
Here is the relevant part of my script, the full script is available here:
var urls = [
["https://www.cnrtl.fr/morphologie/" + keyword, "vtoolbar", "morf_sound"], //best for plural
["https://www.cnrtl.fr/synonymie/" + keyword, "syno_format"],
]
// for test set keyword to any of this word : hibou, tribal, aller, lancer
var resultdiv = document.getElementById("result")
resultdiv.innerText = "requete en cours";
var results = [];
var errors = [];
urls.forEach((item, index) => {
var http = new XMLHttpRequest();
http.onreadystatechange = function () {
if (http.readyState == 4 && http.status == 200) {
parser = new DOMParser();
var ulr1response = parser.parseFromString(http.responseText, "text/html");
if (index == 0) {
//retrieve the data needed, save then in a list and push this list to the main list result
} else if (index == 1) {
//retrieve the data needed, save then in a list and push this list to the main list result
}
// update the DOM
if (results[1] == "") {
resultdiv.innerHTML = results[0].join(", ") + "</br></br>Pas de synonymes trouvés"
} else {
resultdiv.innerHTML = "<b>" + results[0].join(", ") + "</br></br>Synonymes:</b></br>● " + results[1].join('</br>● ')
}
} else {
errors.push(index);
resultdiv.innerText = "Erreur: " + index + " " + http.readyState + " " + http.status;
}
}
http.open("GET", item[0], false);
http.send(null); // null = no parameters
});
// it would be simplier if I could update the DOM here and not in http.onreadystatechange
If you want to execute some code once all requests have succeeded, you can try using Promise.all together with Fetch.
let keyword = "beaucoup";
let parser = new DOMParser();
let urls = [
["https://www.cnrtl.fr/morphologie/" + keyword, "vtoolbar", "morf_sound"], //best for plural
["https://www.cnrtl.fr/synonymie/" + keyword, "syno_format"]
];
let fetchPromises = urls.map(
item => fetch(item[0]).then(
response => parser.parseFromString(response.text(), "text/html")
)
);
Promise.all(fetchPromises).then(
results => {
// code in here executes once all fetchPromises have succeeded
// "results" will be an array of parsed response data
console.log(results);
}
).catch(console.error);
Can someone provide a simple complete example of loading an existing sqlite database not using node.js.
Assume that the sql db is sitting in same location as index.html
example:
I tried this example but "contents" is undefined. Also, I would not know how to access the data in "contents"? I could really use a complete example.
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/database.sqlite', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
var uInt8Array = new Uint8Array(this.response);
var db = new SQL.Database(uInt8Array);
var contents = db.exec("SELECT * FROM my_table");
// contents is now [{columns:['col1','col2',...], values:[[first row], [second row], ...]}]
};
xhr.send();
I know this is old, but here you go my friend. You were right there, just a bit of tweaking. I am not sure if you are using the SQL.js library from GitHub but please do as it solves a lot of the browser security issues and makes the SQL side of things much easier on the brain.
If you didn't create the source or have some UTF issues the exceptions will be thrown. I wrote this in a night so I haven't run more than a few functions but I am assuming callbacks will be required to prevent SQLite issues during async processes. This is my first time using SQLite or the SQL.js library so I just don't know yet.
IMPORTANT!
This is a LOCAL solution only, it has more blatant vulnerabilities than a high school locker room. In no way should this ever be used on anything that is exposed to the internet.
This is all declared at the top of my class, not within a function. This is purposeful as I run multiple queries and didn't want the overhead of loading/unloading the object if it got too large.
Notice the fully qualified path on the source...relative paths didn't work for me.
var xhrLocal = new XMLHttpRequest();
xhrLocal.open('GET', 'http://localhost/mp3/data/media.sqlite', true);
xhrLocal.responseType = 'arraybuffer';
var localData;
xhrLocal.onload = function(e) {
var uInt8Array = new Uint8Array(this.response);
localData = new SQL.Database(uInt8Array);
};
xhrLocal.send();
At this point you have the database loaded into an object called localData and you can query it from anywhere. Here is a query I wrote to get Genre info.
function FillGenreLists() {
var sqlSel = 'SELECT * FROM GenreData';
var data = localData.exec(sqlSel);
var output = [];
$.each(data[0].values, function(key, value)
{
output.push('<option value="'+ value[0] +'">'+ value[1] +'</option>');
});
$('#selGenres').html(output.join(''));
}
The output from the SQL call is generally an array of arrays, don't worry about changing that, just output the result of your SQL call to the console and note the return fields and values, from there just use $.each to your hearts content.
Here is another query, same premise but with the goal of creating a SQL statement to push into MS SQL server and get FreeDB data about artists that are in my local collection.
Note: This could all be done in a single call by querying my local sqlite table, generating the sql and pushing it to the MS SQL using a different conn or even better by utilizing a generic proc but let's keep it simple for now.
function PrepareMSSQLFilteredFreeDBTables(StartLetter, EndLetter, TempTableName) {
var sqlSel = "SELECT * FROM ArtistData WHERE ArtistText BETWEEN '" + StartLetter + "' AND '" + EndLetter + "' ORDER BY ArtistText";
var data = localData.exec(sqlSel);
$('.array-cols').append('SELECT * INTO ' + TempTableName + ' FROM FreeDB WHERE DARTIST IN (');
var iLen = (data[0].values.length - 1);
$.each(data[0].values, function(a, b) {
var sRes;
if (a === iLen) { sRes = "'" + b[1].replace("'", "''") + "')"; }
else { sRes = "'" + b[1].replace("'", "''") + "', "; }
$('.array-cols').append(sRes);
});
}
I wrote a small .js file that has 3 functions in it for easy in-site cookie management. Here is the source for that file:
// Make Cookie
function Bake(name,value) {
var oDate = new Date();
oDate.setYear(oDate.getFullYear()+1);
var oCookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + ';expires=' + oDate.toGMTString() + ';path=/';
document.cookie= oCookie;
}
// Read Cookie
function Eat(name){
name = name.toLowerCase();
var oCrumbles = document.cookie.split(';');
for(var i=0; i<oCrumbles.length;i++)
{
var oPair= oCrumbles[i].split('=');
var oKey = decodeURIComponent(oPair[0].trim().toLowerCase());
var oValue = oPair.length>1?oPair[1]:'';
if(oKey == name)
return decodeURIComponent(oValue);
}
return '';
}
// Delete / Void Cookie
function Burn(name){
Bake(name,'');
}
I put that .js file into my "/models" folder on Cloud9. In my index.js I do have the line: var OCookie = require('../models/oatmealcookie'); to include my custom "library". However, still in index.js, I attempt to call the OCookie.Bake('test','testvalue'); before a redirect, and an error comes up on the page as TypeError: OCookie.Bake is not a function. Any help as to why it's not able to recognise my function as a function?
If that is your whole file, you aren't exporting any of your functions through module.exports. Effectively, your file is run once, and Bake, Eat, and Burn are declared as functions for the module but no other module can use them.
You would need something like:
module.exports = {
Bake: Bake,
Eat: Eat,
Burn: Burn
};
So that other modules can use your functions.
You need an exports.Bake = Bake at the end of the module.
You need to export your functions so that node.js recognises them when you require them.
See here: http://www.sitepoint.com/understanding-module-exports-exports-node-js/ for example.
var exports = module.exports = {
// Make Cookie
'Bake': function (name,value) {
var oDate = new Date();
oDate.setYear(oDate.getFullYear()+1);
var oCookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + ';expires=' + oDate.toGMTString() + ';path=/';
document.cookie= oCookie;
}
// Read Cookie
'Eat': function (name){
name = name.toLowerCase();
var oCrumbles = document.cookie.split(';');
for(var i=0; i<oCrumbles.length;i++)
{
var oPair= oCrumbles[i].split('=');
var oKey = decodeURIComponent(oPair[0].trim().toLowerCase());
var oValue = oPair.length>1?oPair[1]:'';
if(oKey == name)
return decodeURIComponent(oValue);
}
return '';
}
// Delete / Void Cookie
'Burn': function (name){
Bake(name,'');
}
}
By convention though, you would start your function names with lower case.
I am trying to create a file. It works fine when I run the following code segment from the debugger in apps script. However, when I run it real time from the spreadsheet, it says I do not have permission to call createfile. Everything that is logged is identical. The issue is not I do not have authority as I am the only one in the spreadsheet and am the owner. The purpose of the CSV is to move it from my google drive into data for BigQuery
function saveAsCSV(row) { //Doc to Csv
//row = 3; //when this is uncommented and ran from the debugger, it works.
try{
var fileName= Date.now()
fileName = fileName + ".csv";
var csvFile = convertRangeToCsvFile_(fileName,row);
Logger.log(csvFile); //Both times ran on the spreadsheet and from debug equals the same.
DriveApp.createFile(fileName, csvFile);
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("New and Open").getRange("J" + row.toString()).setValue("");
loadCsv(fileName);
}
catch(e){Logger.log("B" + e.message);} //No permission to create file
}
function convertRangeToCsvFile_(csvFileName, r) {
var ws = SpreadsheetApp.getActiveSpreadsheet();
try {
//var data = ws.getValues();
var csvFile = undefined;
var csv = "";
var row = r;
var datArray = Create2DArray(1,19);
datArray[0][0] = ws.getRange("A" + row.toString()).getValue().toString().toUpperCase();
datArray[0][1] = ws.getRange("B"+row.toString()).getValue().toString().toUpperCase();
datArray[0][2] = ws.getRange("C"+row.toString()).getValue().toString().toUpperCase();
datArray[0][3] = ws.getRange("D"+row.toString()).getValue().toString().toUpperCase();
datArray[0][4] = ws.getRange("E"+row.toString()).getValue().toString().toUpperCase();
datArray[0][5] = ws.getRange("F"+row.toString()).getValue().toString().toUpperCase();
datArray[0][6] = ws.getRange("G"+row.toString()).getValue().toString().toUpperCase();
datArray[0][7] = ws.getRange("H"+row.toString()).getValue().toString().toUpperCase();
datArray[0][8] = ws.getRange("I"+row.toString()).getValue().toString().toUpperCase();
datArray[0][9] = new Date(ws.getRange("K"+row.toString()).getValue().toString()).getHours();
datArray[0][10] = new Date(ws.getRange("K"+row.toString()).getValue().toString()).getMinutes();
datArray[0][11] = new Date(ws.getRange("L"+row.toString()).getValue().toString()).getHours();
datArray[0][12] = new Date(ws.getRange("L"+row.toString()).getValue().toString()).getMinutes();
datArray[0][13] = new Date(ws.getRange("M"+row.toString()).getValue().toString()).getHours();
datArray[0][14] = new Date(ws.getRange("M"+row.toString()).getValue().toString()).getMinutes();
datArray[0][15] = new Date(ws.getRange("N"+row.toString()).getValue().toString()).getTime();
datArray[0][16] = new Date(ws.getRange("N"+row.toString()).getValue().toString()).getFullYear();
datArray[0][17] = new Date(ws.getRange("N"+row.toString()).getValue().toString()).getMonth();
datArray[0][18] = new Date(ws.getRange("N"+row.toString()).getValue().toString()).getDate();
for(var i = 0; i < 19; i++){
if(datArray[0][i] == ""){if(i > 9){datArray[0][i] = 0;} else{datArray[0][i] = "nil";} }
if(i < 18){csv += '"' + datArray[0][i] + '"' + ",";}
else{ csv += '"' + datArray[0][i] + '"'; }
}
Logger.log("A " + csv);
Logger.log(csv + "\n" + datArray[0].join(","));
csvFile = csv;
return csvFile;
}
catch(err) {
Logger.log("C" + err);
Browser.msgBox(err);
}
}
You mention in your comment on my answer that you are using onEdit to trigger the script. Since this is a Simple Trigger, your current approach will not work. When you use simple triggers to run an Apps Script, it runs in a sandbox with reduced permissions.
See: https://developers.google.com/apps-script/guides/triggers/#restrictions
The best I can recommend is create a custom menu option with a UI popup asking for the row number to export. If the code is triggered from a menu by the user, it runs with full permission to access that users account.
Depending on your use-case, a scheduled trigger might work too. It could run every 10 minutes or every Hour and export any changes to the spreadsheet. In this case the Apps Script runs as you, with permission to access your account, and the resulting CSV would be created on your drive.
Details on how to create a custom menu: https://developers.google.com/apps-script/guides/triggers/#onopen
Details on how to create a form for the user: https://developers.google.com/apps-script/guides/ui-service
Details on time driven triggers: https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers
I'm working on a FF extension that in short, loads dynamic images into a sidebar. The ID's that I get are from a JSON response, and are stored in a global variable declared in the same .js file as I intend to use it. My problem is when I try to simulate paging through my results. I load the sidebar using my global variable and everything is ok. When I try to then move on to the next set of images to display using the ID's i've stored in my global variable it failes due to my variable having been completely reset. I'll see if I can give a rough view of my code:
var searchVars = {
'keyword': "",
'totalResults': 0,
'imgIds': [],
'cIds': [],
'curPg': "1",
'dispStartIdx': 0,
'dispEndIdx': 4,
'dispPerPg': 5,
toString: function() {
return this.keyword + ", " +
this.totalResults + ", " +
this.imgIds + ", " +
this.cIds + ", " +
this.curPg + ", " +
this.dispStartIdx + ", " +
this.dispEndIdx + ", " +
this.dispPerPg;
}
};
var corbisquicksearch = {
onSearch: function () {
cqsearch.resetSearch(); //Resets my global variable every search
searchVars.keyword = cqsearch.getSelectedText(); //searchVars is my global variable im having trouble with
cqsearch.extendImageCache();
}
extendImageCache: function() {
var postToURL = 'http://www.agenericurl.com/Search?';
var keyword = searchVars.keyword;
var p = 1; //Page Offset for ID's returned
var size = 200; //Number of ID's returned in the response set
var query = "searchQuery=" + encodeURIComponent("q=" + keyword + "&p= " + p +"&s=" + size);
var request = new XMLHttpRequest();
request.open('post', postToURL + query, true);
request.onreadystatechange = function (aEvt) {
if (request.readyState == 4) {
alert(1);
if(request.status == 200) {
alert(2);
var responseInJSON = JSON.parse(request.responseText);
searchVars.totalResults = responseInJSON.ResultsCount;
var i = searchVars.imgIds.length;
var lastResult = i + responseInJSON.SearchResultImages.length;
while (i < lastResult) {
searchVars.imgIds[i] = responseInJSON.SearchResultImages[i].ImageId;
searchVars.cIds[i] = responseInJSON.SearchResultImages[i].CorbisId;
i++;
}
cqsearch.loadSidebar();
}
else {
dump("Error loading page\n");
}
}
};
request.send();
},
loadSidebar: function() {
//Initializing Env Variables
var sidebar = document.getElementById("sidebar");
var sidebarDoc = sidebar.contentDocument || document;
var searchInfoBox = sidebarDoc.getElementById("search_info");
var resultsBox = sidebarDoc.getElementById("img_results");
var pagingInfoBox = sidebarDoc.getElementById("paging_info");
//Loading up the search information
var searchInfo = "Displaying Results for <b>{0}<b/><br>Showing <b>{1} - {2}</b> of <b>{3}</b>";
var args = [searchVars.keyword, searchVars.dispStartIdx, searchVars.dispEndIdx, searchVars.totalResults];
var infoLbl = document.createElement("label");
infoLbl.setAttribute("value", cqsearch.strFormat(searchInfo, args));
searchInfoBox.appendChild(infoLbl);
while (resultsBox.firstChild) {
resultsBox.removeChild(resultsBox.firstChild);
}
//Loading up the image results
var i = searchVars.dispPerPg * (searchVars.curPg - 1);
var lastDisplayed = (searchVars.curPg * searchVars.dispPerPg) - 1;
alert("length" + searchVars.toString());
while (i <= lastDisplayed) {
var imageID = searchVars.imgIds[i];
var cID = searchVars.cIds[i];
var imgSrc = cqsearch.createMediaUrlParams(imageID, 'thumb', cID, false).url; //thumb, 170, hover
var img = document.createElement("image");
img.setAttribute("src", imgSrc);
alert(imgSrc);
img.setAttribute("class", "img");
var idDelimiter = "_image";
var id = cID + idDelimiter;
img.id = id;
img.addEventListener("click", function () {
cqsearch.openEnlargementPage(this.id.substring(0, this.id.indexOf(idDelimiter)));
}, false);
var imgBox = document.createElement("box");
imgBox.setAttribute("class", "imgContainer");
imgBox.appendChild(img);
resultsBox.appendChild(imgBox);
i++;
}
//Loading up paging info and functionality
var prevBtn = document.createElement("button");
prevBtn.setAttribute("label", "Previous");
prevBtn.setAttribute("oncommand", "cqsearch.prevPage()");
var nextBtn = document.createElement("button");
nextBtn.setAttribute("label", "Next");
nextBtn.setAttribute("oncommand", "cqsearch.nextPage()");
pagingInfoBox.appendChild(prevBtn);
pagingInfoBox.appendChild(nextBtn);
},
nextPage: function() {
searchVars.curPg++;
alert(searchVars.imgIds);
cqsearch.loadSidebar();
},
};
I realize its a lot of code, and I didn't post every function I have, and no, this specific URL does not work. Everything not included works fine, and does exactly what its supposed too, and nothing more which is why I left it out. But if anyone could shed some light on why my global variable is being cleared between my initial load of the sidebar, and when I click to go to the next page, I would greatly appreciate it.
If you need me to add something or clarify something please let me know and I will do so! I will probably end up sliming this code down and removing irrelevant parts.
Thanks!
If you simply want a place to store some global variables for a session, then a JavaScript Module would probably work.
Would you be able to use the client side storage to store the global variable? you will then, not lose it on page loads or refresh. You could either use this to debug and see if you are getting a page refresh because sometimes extensions are fickle and you don't even notice the refresh, but if you store the variable as a key value pair in web storage you might get past this.
localStorage.setItem('imgId', '5');
to set your key/value pair
localStorage.getItem('imgId');
to retrieve your key/value pair
Then you can set a new local storage for each series of pictures that has been displayed to the client based on the last number that is set in local storage.