Problem with Javascript localStorage for-loop - javascript

I am using localStorage to store incoming message: {"lastTid":22,"Hour":10,"min":31,"Second":34} where the time is increasing in every new incoming object, e.g. next object: {"lastTid":22,"Hour":10,"min":31,"Second":35}.
The new messages are being stored in localStorage correctly as seen here:
My Javascript code implements the localStorage.getItem and .setItem correctly to retrieve and get my incoming data message, which will be rendered with .innerHTML to var hst, which hst = document.getElementById("highscores"). all code below
The ElementId "highscores" is assigned to <table> tag in my html and comes out as seen here:
#js code snippet#
var hst = document.getElementById("highscores");
function onMessageArrived(message) {
var data = JSON.parse(message.payloadString);
var highScores = [data];
var array = JSON.parse(localStorage.getItem('highscores') || '[]');
array.push(highScores);
localStorage.setItem('highscores', JSON.stringify(array));
for (var i = 0; i < array.length; i++) {
hst.innerHTML += "<tr><td>" + array[i][0].Hour + ":"+ array[i][0].min + ":" + array[i][0].Second + "</td><td>" + array[i][0].lastTid + "</td></tr>";
}
};
My problem is, because of my For loop, when a new message arrives, my loop goes through and renders the previous message and the new message, while the old message is still in the table(please see the table image above). It is displaying object 0, object 0 & 1, then object 0, 1 & 2 all in the same table. Instead of 0, then 1 after, then 2 ... n.
I wish to display the values in proper order and also keep those values even after the page is reloaded. Desired result:

Don't append directly hst.innerHTML +=...
try something like this
let html = ''
for (var i = 0; i < array.length; i++) {
html += "<tr><td>" + array[i][0].Hour + ":"+ array[i][0].min + ":" + array[i][0].Second + "</td><td>" + array[i][0].lastTid + "</td></tr>";
}
hst.innerHTML = html

I think the problem is your always appending the array content at hst.innerHTML each incoming message, so the previous loop content remains. It's also not recommended for performance to manipulate DOM inside loops.
let res = '';
for (var i = 0, l = array.length; i < l; i++) {
res += '<tr><td>' + array[i][0].Hour + ':' + array[i][0].min + ':' + array[i][0].Second + '</td><td>' + array[i][0].lastTid + '</td></tr>';
}
hst.innerHTML = res; // You overwrite the content instead of appending each time.

Related

Acrobat displays corrupted PDF documents that conatin xref streams, after preparation for signing

We are working on a webapp that signs PDF documents. The workflow looks something like this:
PDF is uploaded to the server
Acroform is appended, and the necessary annotations are added, along with other XObjects required to make a signature
Old Xref Tables/Streams are removed
New Xref Tables/Streams are generated
Digest is calculated over the part of the document marked by the ByteRange
Digest is send to the backend for signing
The resulting signature is embedded into the PDF on the client
The PDFs that use XRef tables are signed fine. However, we also needed to add the possibility to sign the PDFs that use XRef Streams. I know that in steps 3 and 4 it may be wiser to make incremental updates instead of removing all of the XRef Streams/Tables and making a new one. However, since we already had a flow like that working for the XRef Tables, we decided to keep the consistency for now, and do it same for the XRef Streams.
It seems that there are some problems with the PDFs where XRef Streams are used. The old ones are removed fine, and also the new one is generated and on the first view looks fine. However, when I try to open the document in Acrobat I get a message that the document is corrupted.
Here's the relevant part of the code that creates XRef Stream in case XRef streams are used:
{
.
.
.
const xrefStream = this.createXrefStream(pdf.xref.entries);
const ids = pdf.xref.topDict.map.ID;
const info = pdf.xref.topDict.map.Info;
const rootEntry = pdf.xref.topDict.map.Root;
const xrefStreamObj = '\n' + xrefStreamEntry + ' 0 obj\n' +
'<</DecodeParms' + '\n' +
'<</Columns 5/Predictor 12>>' + '\n' +
'/Filter/FlateDecode' + '\n' +
'/ID[<' + this.strHex(ids[0]) + '><' + this.strHex(ids[1]) + '>]' + '\n' +
'/Info ' + info.num + ' ' + info.gen + ' R' + '\n' +
'/Length ' + xrefStream.length + '\n' +
'/Root ' + rootEntry.num + ' ' + rootEntry.gen + ' R' + '\n' +
'/Size ' + (xrefStreamEntry + 1) + '\n' +
'/Type/XRef' + '\n' +
'/W[1 3 1]>>' + '\n' +
'stream\n' +
xrefStream + '\n' +
'endstream' + '\n' +
'startxref' + '\n' +
(array.length + 1) + '\n' +
'%%EOF';
array = this.insertIntoArray(array, array.length, xrefStreamObj);
.
.
.
}
private createXrefStream(entries: any) {
const xrefStreamData = [];
entries.forEach((entry, i) => {
xrefStreamData[i] = [];
if (entry.free) {
xrefStreamData[i][0] = 0;
xrefStreamData[i][1] = entry.offset;
xrefStreamData[i][2] = entry.gen;
} else if (entry.uncompressed) {
xrefStreamData[i][0] = 1;
xrefStreamData[i][1] = entry.offset;
xrefStreamData[i][2] = entry.gen;
} else {
xrefStreamData[i][0] = 2;
xrefStreamData[i][1] = entry.offset;
xrefStreamData[i][2] = entry.gen;
}
});
const xrefStreamBytes = [];
for (let i = 0; i < xrefStreamData.length; i++) {
xrefStreamBytes[i] = [];
xrefStreamBytes[i][0] = xrefStreamData[i][0];
xrefStreamBytes[i][4] = xrefStreamData[i][2];
let hexOffset = xrefStreamData[i][1].toString(16);
const hexOffsetLength = hexOffset.length;
for (let j = 0; j < 6 - hexOffsetLength; j++) {
hexOffset = '0' + hexOffset;
}
const offsetBytes = this.hex2Uint8Array(hexOffset);
for (let j = 0; j < offsetBytes.length; j++) {
xrefStreamBytes[i][j + 1] = offsetBytes[j];
}
}
xrefStreamBytes[-1] = [];
for (let i = 0; i < xrefStreamBytes.length; i++) {
xrefStreamBytes[-1][i] = 0;
}
const encodedXrerStreamBytes = [];
for (let i = 0; i < xrefStreamBytes.length; i++) {
encodedXrerStreamBytes[i] = [];
encodedXrerStreamBytes[i][0] = 2;
for (let j = 1; j < xrefStreamBytes[i].length; j++) {
encodedXrerStreamBytes[i][j] = Math.abs(xrefStreamBytes[i][j - 1] - xrefStreamBytes[i - 1][j - 1]);
}
}
const linearXrefStreamBytes = [];
let counter = 0;
for (let i = 0; i < encodedXrerStreamBytes.length; i++) {
for (let j = 0; j < encodedXrerStreamBytes[i].length; j++) {
linearXrefStreamBytes[counter] = encodedXrerStreamBytes[i][j];
counter++;
}
}
const deflated = pako.deflate(linearXrefStreamBytes);
const xrefStream = new TextDecoder('utf-8').decode(deflated);
return xrefStream;
}
Here's a signed PDF that's making problems here. I don't know what to do, debugging PDFs is hard since I can't get any info about the errors.
EDIT: There was an error in size attribute in the xref stream dictionary. The size was wrongly calculated. The actual number of xref entries in the xref stream is 10596 which seems ok, as it's the greatest object number in the given pdf. Unfortunately, that wasn't the only problem and I Acrobat still sees the PDF as corrupted. There is probably some error in the xref stream itself as #mkl has suggested but I can't find it.
**EDIT2:**I found another error in my xref stream. I updated the code with the corrected version and also the PDF file. However, Acrobat still sees the pdf as corrupted
EDIT3: I found another problem. The Columns value in my DecodeParams dictionary was set to 4, and the W value was [1 3 1] meaning that each row (each xref entry) has 1+3+1 = 5 bytes. I corrected this, but I still get the error. I updated the link.

Compare arrays with JS and print result in matrix table

I am trying to make a matrix table by combining values from array. First array holds information about user for specific day. Second array holds list of users, third array holds days in month.
First I get data from api.
var odgovor = JSON.parse(http.responseText);
dataSetMoj = odgovor.urvPoDanuZaMjesec;
dataSetMojUposlenik = odgovor.uposlenici;
Get days for month
var mjesecImaDana = getDaysInMonth(1,2018);
var daniMjeseca = [];
for (i = 1; i <= mjesecImaDana; i++) {
daniMjeseca.push([i]);
}
Then I create table header
var k = '<thead>';
k +='<tr><th style="background-color: #f8f7f7" >' + rb + '</th>';
k +='<th style="background-color: #f8f7f7" >' + up + '</th>';
daniMjeseca.forEach(function(daysInmonth){
k += '<th style="background-color: #f8f7f7"> '+ daysInmonth +'</th>';
});
k += '</tr></thead>';
Table header display correct information, as could be seen in img.
Array rezultati holds information about each user on specific day
var rezultati = [];
for(i = 0;i < dataSetMoj.length; i++){
rezultati.push(dataSetMoj[i].ime+'-'+dataSetMoj[i].datum+'-'+dataSetMoj[i].urv);
}
Then I create table body, In table body I should match user with date.
k += '<tbody>'
for(i = 0;i < dataSetMojUposlenik.length; i++){
k+='<tr>';
k+='<td> ' + a++ + '</td>';
k+='<td> ' + dataSetMojUposlenik[i].ime + '</td>';
daniMjeseca.forEach(function(daysInmonth){
var dataSetVar = dataSetMojUposlenik[i].ime +'-'+daysInmonth;
//here I should check if variable 'datSetVar' is set in array rezultati, if yes I should print dataSetMoj[i].urv from rezultati
if(rezultati == dataSetVar ){
k+='<td> ' + dataSetMoj[i].urv + '</td>'; // It does not work, but I have no idea. I could not find solution any hint would help me.
}
else{
k+='<td> ' + c + '</td>';
}
});
k+='</tr>';
}
k+='</tbody>';
document.getElementById('tableData').innerHTML = k;
Array rezultati holds three element dataSetVar two. I am not able to make comparison got wrong answers.

Iterating every other AJAX object

I have a JSON encoded AJAX object array and I need to iterate every other one. I'm new to jQuery and JS and are unsure how to do this. I need the numbers to be the option value and the string name as the object name.
The AJAX object (note although in this example the numerals are in order they might not be because the integers are an auto_id from a database and numbers might be skipped or out of order:
{"data":["1","chicken","2","beef","3","fish","4","pork","5","turkey","6","lamb","7","vennison","8","duck"],"error":false}
I tried the following code below which doesn't work. I tried to do a search for this answer but couldn't find it. TIA. Currently, newOption is returning "undefined".
$.each(data.data, function(i,s){
var count=s[i];
var newOption = s[i+1];
output += '<option value="' + count + '">' + newOption + '</option>';
i++;
});
Use a for loop, but increment the counter by 2 each loop:
var data = {"data":["1","chicken","2","beef","3","fish","4","pork","5","turkey","6","lamb","7","vennison","8","duck"],"error":false}
var output = '';
for(i = 0; i < data.data.length; i+=2) {
output += '<option value="' + data.data[i] + '">' + data.data[i+1] + '</option>';
}
https://jsfiddle.net/go98npym/
When Iterating over a Array using jQuery.each the first param is index and the second one is value in the index itself.
Instead of using jQuery use plain javascript.
try the following:
var d = data.data;
var output = "";
for (var i = 0 ; i< d.length; i=i+2) {
var count = d[i];
var newOption = d[i+1];
output += "<option value='"+count+"'>"+newOption+"</option>";
}
Can you use an if statement with modulus? And then initialize your count outside the loop.
var count = 0;
$.each(data.data, function(i,element){
if (i % 2 == 0) {
count++;
output += '<option value="' + count + '">' + element + '</option>';
}
i++;
});

Browser update element takes long when looping array of 2000 elements with new values

I am refactoring my code so my search mechanism search results with will get results from ajax/json. But by doing this browser seems to stopped responding(Current page tab only) until the data is populated in select tag
Here is what i am doing.
for (i = 0; i < jsonData["outlet"].length; i++) {
outlet.innerHTML += "<option value=" + jsonData["outlet"][i] + ">" + jsonData["outlet"][i] + "</option>";
}
Lenght of those json data are around 2000
Buffer it all in memory in a string. This is relatively fast.
Then,when the string is complete output it to the HTML.
In your current code you are forcing a graphics reflow with every iteration. Every iteration the page has to recalculate the entire page layout. This costs a lot of cpu cycles.
function getBuffer(jsonData) {
var buffer = [];
for (i = 0; i < jsonData["outlet"].length; i++) {
buffer[i] = "<option value=" + jsonData["outlet"][i] + ">" + jsonData["outlet"][i] + "</option>";
}
return buffer.join('');
}
var jsonData = {outlet:["foo","bar","baz"]};
for(flood = 3;flood<10000;flood++) {
jsonData.outlet[flood] = 'fooding '+flood;
}
var outlet = document.getElementById('outlet');
outlet.innerHTML = getBuffer(jsonData);
<select id="outlet"></select>

How to create an array of variables from an array in Javascript

I have a variable called "information" which creates a multi-dimensional array. For each row in the array, I want to return a variable whose name is the first value in the array. In other words, given the 'information' array below, I'd want the following output:
var lunalovegood = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Luna Lovegood is a Ravenclaw!;
var dracomalfoy = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Draco Malfoy is a Slythering!;;
var hermionegranger = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Hermione Granger is a Gryffindor!;;
In other words, I want to be able to work with each of the elements in the 'information' array to create some markup. I already know how to get the information I need given the information array, but as you can see below I'd have to declare separate variables for each of the names.
for (var i = 0; i < information.length; i++) {
var htmlString = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Luna Lovegood is a Ravenclaw!
$('div').html(htmlString);
} //end for loop
var information = [
['lunalovegood', 'Ravenclaw', 'Luna', 'Lovegood', '(chaser)', 'lovegood.jpg', 4]
['dracomalfoy', 'Slytherin', 'Draco', 'Malfoy', '(seeker)', 'malfoy.jpg', 2],
['hermionegranger', 'Gryffindor', 'Hermione', 'Granger', '(none)', 'granger.jpg', 3],
];
The javascript below creates three variables called 'lunalovegood', 'dracomalfoy', and 'hermionegrange', but it's the long way of creating variables. How do I create these variables, one for each row in the array, by looping through the 0th indexed element in the 'information' array?
var myVariables = {}
,varNames = ["lunalovegood","dracomalfoy","hermionegranger"];
for (var i=0;i<varNames.length;i+=1){
myVariables[varNames[i]] = 0;
console.log(lunalovegood);
}
Your current approach just needs a most minor tweak to not require the second array.
var students = {}, i;
for (i = 0; i < information.length; ++i)
students[information[i][0]] = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i][1] + '!';
Now the key is set by taking the first item of the Array. You would then do the following for your text,
students['lunalovegood']; // "Luna Lovegood is a Ravenclaw!"
You're also missing a , in your information literal.
This should help you:
Every variable in the global scope can be accessed as a string property of the window object
var myvariable = 4;
alert(window["myvariable"]); // will alert 4
window["newvariable"] = 6;
alert(newvariable); // will alert 6
I agree with Bergi. Variables should represent a fixed finite set of members defined by code; data (as in the contents of a list) should generally not introduce new variables.
As such, here is the approach I would recommend (note that I've added a bit more than the "minimum required"; good luck!):
// Using a function makes it easy to change details and avoid leaking
// variables accidentally.
function loadWizards(information) {
var wizards = [];
for (var i = 0; i < information.length; i++) {
var info = information[i];
var name = info[0];
// Mapping to named properties means we can forget about indices!
wizards[name] = { // <- use Name to map to our Wizard object
house: info[1],
// ..
image: info[7]
};
}
return wizards;
}
// I have no idea if they are wizards, but give variables useful names.
// 'information' is too generic.
var wizards = loadWizards(information);
// Then later on, use it as:
alert("Hello " + wizards['hermionegranger'].name + "!")
// ^-- property access by Name
var formattedInfo = {};
$.each(information, function (i, v) {
formattedInfo[v[0]] = v[2] + ' ' + v[3] + ' is a ' + v[1];
});
there is a missing comma at the end of the 1st line of your definition of information.
BTW, I like Harry Potter very much.

Categories

Resources