Unexpected Identifier while selecting elements from array - javascript

I'm tearing my hair out about a Syntax Error: Unexpected Identifier that I can't figure out. I know what the error means, but as far as I can tell there's nothing wrong.
I've posted the entirety of the script I'm using; what the code is meant to do is allow a user to step through a replay of a gomoku-like game one move at a time. The game data is stored in a csv file that has a row for every move and contains multiple games. Games are identified by an index value.
var replayArray = [],
rawData=[[]];
function importData(matchID,gI) {
var dataPromise = $.ajax({
url:"./data/" + matchID + ".csv",
dataType: 'text'
})
dataPromise.then(function(data) {
rawData = data;
rawData = String(rawData);
rawData = rawData.split(/\n/);
for (h = 0; h < rawData.length; h++){
rawData[h] = String(rawData[h]).split(",");
}
}).done(function(data){
dataToArray(gI,actionReplayKeydown);
})
}
function dataToArray(gI,cb) {
var f = 0;
var g = 0;
for (var i = 0; i < rawData.length; i++) {
var turnArray = [];
if (parseInt(eval(rawData[i][1])) === gI) {
turnArray[0] = colorToNumber(eval(rawData[i][5]));
turnArray[1] = parseInt(eval(rawData[i][6]));
replayArray[g] = turnArray;
g++;
} else {
doNothing();
}
}
cb(replayArray);
}
The dataToArray function is where the problem occurs, in the line
if (parseInt(eval(rawData[i][1])) === gI) {
I think dev tools has been indicating the problem occurs at rawData[i][1], but rawData is a two dimensional array and the indexing should work fine (the first column of rawData contains the game index, and I want all rows where the value of the game index equals the index of the queried game).
The rest of the code follows but is not afaik problematic.
function colorToNumber(inputColor) {
if (inputColor === "B" ) {
return 0
} else {
return 1
}
}
function actionReplay(inputArray) {
addStone(parseInt(inputArray[f][1]),parseInt(inputArray[f][0]));
f++;
$('#whiteLastMove').remove();
$('#blackLastMove').remove();
if ((f+1)===inputArray.length){
$(document).off('keyup').on('keyup',function(e){
if (e.keyCode === 32) {
clearBoard();
createTiles(M,N);
replayArray = [];
rawData="";
}
});
}
}
function actionReplayKeydown() {
$(document).off('keyup').on('keyup',function(e) {
if (e.keyCode === 13) {
actionReplay(replayArray);
evaluateWin(0);
evaluateWin(1);
} else if (e.keyCode === 32) {
clearBoard();
createTiles(M,N);
replayArray = [];
rawData="";
} else {
doNothing();
}
});
}
function playReplay(matchID,gI) {
openCurtain(doNothing);
importData(matchID,gI);
}
I'm sure I'm missing something obvious, but I'm just not figuring it out on my own.

The issue is that there is a js syntax error in the value of rawData[i][1]. If you use your debugger you can see the value and check whether it's valid js for eval to execute.

Related

Javascript Loop Not Finished before If Statement Runs

I have been stuck on this issue for some time now. I am calling an API - get the results just fine. I am saving the values to an array. The problem which I am encountering is trying to get specific values from the array. I have a for in loop running which takes time, so when the if statement is ran the loop hasn't reached that value. If I use Postman, I see that the value exists, its just the loop doesn't execute in time. Here is my code:
var msg = {};
var embed = {};
var link = {};
var msgIn = [];
var rel = [];
return SkypeService.getEvent(msg).then(function (result) {
msg.eventsNext = result._links.next.href;
if (result && result.sender && result.sender.length > 0) {
if (result.sender) {
for (var item in result.sender) {
var event = result.sender[item].events;
for (var key in event) {
embed = event[key]._embedded;
msgIn.push(embed);
}
for (var key in event) {
link = event[key].link;
rel.push(link);
}
// console.log(Object.entries(msgIn))
if(rel['rel'] == 'message') {
console.log("message is there")
if(msgIn.message) {
console.log("links exist")
if(msgIn.message.direction == "Incoming") {
console.log("direction is there")
msg.participant = msgIn.message._links.participant.href;
msg.contMsg = msgIn.message._links.messaging.href;
msg.msgIn = msgIn.message._links.plainMessage.href;
break;
}
}
}
if(rel['rel'] == "messagingInvitation"){
console.log("invite there")
if(msgIn.messagingInvitation && msgIn.messagingInvitation.state !== "Failed") {
console.log("invite link")
if(msgIn.messagingInvitation.direction == "incoming") {
console.log("direction invite")
msg.msgInviteState = msgIn.messagingInvitation._links.state;
msg.acceptInvite = msgIn.messagingInvitation._links['accept'].href;
msg.msgIn = msgIn.messagingInvitation._links.message.href;
break;
}
}
}
if(rel['rel'] == 'messaging') {
console.log('messaging there')
if(msgIn.messaging) {
if(msgIn.messaging.state == "Disconnected") {
console.log("msgn Disconnected")
msg.addMsg = msgIn.messaging._links.addMessaging.href;
break;
}
}
}
}
}
}
console.log(msg)
})
Also, I've attached a screenshot of my local host printing the msgIn which shows that the keys exists.
When I test the code running sails lift, I can see that msgIn prints a couple of times each one increasing in length. This is what makes me think the for loop has not completed by the time the if statement runs.
Please help - I really need for this to be resolved. I need to capture the links so that I can use those in the next step.
Thanks.
I have resolved my issue by making changes to the code. Here is the new version:
return
SkypeService.getEvent(msg).then(function
(result) {
msg.eventsNext = result._links.next.href;
if (result.sender) {
for (var item in result.sender) {
var event = result.sender[item].events;
for (var key in event) {
embed = event[key]._embedded;
link = event[key].link;
};
if(link['rel'] == 'message') {
console.log("message is there")
if(embed.message) {
console.log("links exist")
if(embed.message.direction == "Incoming") {
console.log("direction is there")
msg.participant = embed.message._links.participant.href;
msg.contMsg = embed.message._links.messaging.href;
msg.msgIn = embed.message._links.plainMessage.href;
break;
}
}
};
if(link['rel'] == "messagingInvitation"){
console.log("invite there")
if(embed.messagingInvitation) {
console.log("invite link")
if(embed.messagingInvitation.direction == "incoming") {
console.log("direction invite")
msg.msgInviteState = embed.messagingInvitation._links.state;
msg.acceptInvite = embed.messagingInvitation._links['accept'].href;
msg.msgIn = embed.messagingInvitation._links.message.href;
break;
}
}
};
if(link['rel'] == 'messaging') {
console.log('messaging there')
if(embed.messaging) {
if(embed.messaging.state == "Disconnected") {
console.log("msgn Disconnected")
msg.addMsg = embed.messaging._links.addMessaging.href;
break;
}
}
};
console.log(msg)
};
};
});
I have removed the result validation and simplified the for (var key in event) to handle both operations in one. Also, I have removed the arrays which I was pushing the values into as I was not using that. That may have been the time consuming factor which was preventing me from getting the direction validated.

Getting nested obj value

Given the following obj:
var inputMapping = {
nonNestedItem: "someItem here",
sections: {
general: "Some general section information"
}
};
I'm writing a function to get that data by passing in a string "nonNestedItem" or in the nested case "sections.general". I'm having to use an eval and I was wondering if there was maybe a better way to do this.
Here is what I have so far and it works okay. But improve!
function getNode(name) {
var n = name.split(".");
if (n.length === 1) {
n = name[0];
} else {
var isValid = true,
evalStr = 'inputMapping';
for (var i=0;i<n.length;i++) {
evalStr += '["'+ n[i] +'"]';
if (eval(evalStr) === undefined) {
isValid = false;
break;
}
}
if (isValid) {
// Do something like return the value
}
}
}
Linky to Jsbin
You can use Array.prototype.reduce function like this
var accessString = "sections.general";
console.log(accessString.split(".").reduce(function(previous, current) {
return previous[current];
}, inputMapping));
Output
Some general section information
If your environment doesn't support reduce, you can use this recursive version
function getNestedItem(currentObject, listOfKeys) {
if (listOfKeys.length === 0 || !currentObject) {
return currentObject;
}
return getNestedItem(currentObject[listOfKeys[0]], listOfKeys.slice(1));
}
console.log(getNestedItem(inputMapping, "sections.general".split(".")));
You don't need to use eval() here. You can just use [] to get values from an object. Use a temp object to hold the current value, then update it each time you need the next key.
function getNode(mapping, name) {
var n = name.split(".");
if (n.length === 1) {
return mapping[name];
} else {
var tmp = mapping;
for (var i = 0; i < n.length; i++) {
tmp = tmp[n[i]];
}
return tmp;
}
}

Get rid of "PageMap asked for range which it does not have" exception

Occassionally I get the exception "PageMap asked for range which it does not have" from my Ext Js 4.2.1 infinite scrolling grid. It is raised in data/PageMap.js on line 211. Of course one should not ask for non-existing entries, but this is sometimes done by the framework itself. Seems to be somehow connected to adding/removing records or reloading the grid. There are already some threads on this topic in the Sencha forum, e.g. this, but no killer solution or bugfix was proposed yet.
Meanwhile, I have to keep this exception from the users' eyes. What would be a good way to do so? Tricky thing is that it is sometimes provoked just by the user moving the scrollbar, so there is no single line of my code directly involved.
I found the root cause to be that when it's rendering rows, it determines if it's before a selected row. If it's working on the last row, it still looks for row + 1. (Ext.view.Table:931 in 4.2.1)
My simple solution is to just make it return false:
Ext.override(Ext.selection.RowModel,
{
isRowSelected: function (record, index)
{
try
{
return this.isSelected(record);
}
catch (e)
{
return false;
}
}
});
Christoph,
I have similar troubles with "PageMap asked for range which it does not have" during asynchronuous refreshing of grids. I catched some of sources of errors in the ExtJS 4.2.1 code and created simple override, that works for me. You can try if it will work for you. I will be happy for your feedback.
Ext.override(Ext.view.Table, {
getRecord: function (node) {
node = this.getNode(node);
if (node) {
var recordIndex = node.getAttribute('data-recordIndex');
if (recordIndex) {
recordIndex = parseInt(recordIndex, 10);
if (recordIndex > -1) {
// Eliminates one of sources of "PageMap asked for range which it does not have" error
if (this.store.getCount() > 0) {
return this.store.data.getAt(recordIndex);
}
}
}
return this.dataSource.data.get(node.getAttribute('data-recordId'));
}
},
renderRow: function (record, rowIdx, out) {
var me = this,
isMetadataRecord = rowIdx === -1,
selModel = me.selModel,
rowValues = me.rowValues,
itemClasses = rowValues.itemClasses,
rowClasses = rowValues.rowClasses,
cls,
rowTpl = me.rowTpl;
rowValues.record = record;
rowValues.recordId = record.internalId;
rowValues.recordIndex = rowIdx;
rowValues.rowId = me.getRowId(record);
rowValues.itemCls = rowValues.rowCls = '';
if (!rowValues.columns) {
rowValues.columns = me.ownerCt.columnManager.getColumns();
}
itemClasses.length = rowClasses.length = 0;
if (!isMetadataRecord) {
itemClasses[0] = Ext.baseCSSPrefix + "grid-row";
if (selModel && selModel.isRowSelected) {
var storeRows = this.getStore().getCount();
// Eliminates one of sources of "PageMap asked for range which it does not have" error
if (rowIdx + 1 < storeRows) {
if (selModel.isRowSelected(rowIdx + 1)) {
itemClasses.push(me.beforeSelectedItemCls);
}
}
if (selModel.isRowSelected(record)) {
itemClasses.push(me.selectedItemCls);
}
}
if (me.stripeRows && rowIdx % 2 !== 0) {
rowClasses.push(me.altRowCls);
}
if (me.getRowClass) {
cls = me.getRowClass(record, rowIdx, null, me.dataSource);
if (cls) {
rowClasses.push(cls);
}
}
}
if (out) {
rowTpl.applyOut(rowValues, out);
} else {
return rowTpl.apply(rowValues);
}
}
});
all these codes don't work for me, after many debugging I wrote this override which solve the problem.
Ext.define('overrides.LruCache', {
override: 'Ext.util.LruCache',
// private. Only used by internal methods.
unlinkEntry: function (entry) {
// Stitch the list back up.
if (entry) {
if (this.last && this.last.key == entry.key)
this.last = entry.prev;
if (this.first && this.first.key == entry.key)
this.first = entry.next;
if (entry.next) {
entry.next.prev = entry.prev;
} else {
this.last = entry.prev;
}
if (entry.prev) {
entry.prev.next = entry.next;
} else {
this.first = entry.next;
}
entry.prev = entry.next = null;
}
}
});
This is my solution for my specific case with the same error
it somehow lost DOM element for child
this code fix that
Ext.define('override.Ext.view.Table', {
/**
* Returns the node given the passed Record, or index or node.
* #param {HTMLElement/String/Number/Ext.data.Model} nodeInfo The node or record
* #param {Boolean} [dataRow] `true` to return the data row (not the top level row if wrapped), `false`
* to return the top level row.
* #return {HTMLElement} The node or null if it wasn't found
*/
override: 'Ext.view.Table',
getNode: function (nodeInfo, dataRow) {
// if (!dataRow) dataRow = false
var fly,
result = this.callParent(arguments)
if (result && result.tagName) {
if (dataRow) {
if (!(fly = Ext.fly(result)).is(this.dataRowSelector)) {
result = fly.down(this.dataRowSelector, true)
}
} else if (dataRow === false) {
if (!(fly = Ext.fly(result)).is(this.itemSelector)) {
result = fly.up(this.itemSelector, null, true)
}
if (this.xtype == 'gridview' && !this.body.dom.querySelector(`#${result.id}`)) {
result = null
}
}
}
return result
},
})

Mutable variable is accessible from closure. How can I fix this?

I am using Typeahead by twitter. I am running into this warning from Intellij. This is causing the "window.location.href" for each link to be the last item in my list of items.
How can I fix my code?
Below is my code:
AutoSuggest.prototype.config = function () {
var me = this;
var comp, options;
var gotoUrl = "/{0}/{1}";
var imgurl = '<img src="/icon/{0}.gif"/>';
var target;
for (var i = 0; i < me.targets.length; i++) {
target = me.targets[i];
if ($("#" + target.inputId).length != 0) {
options = {
source: function (query, process) { // where to get the data
process(me.results);
},
// set max results to display
items: 10,
matcher: function (item) { // how to make sure the result select is correct/matching
// we check the query against the ticker then the company name
comp = me.map[item];
var symbol = comp.s.toLowerCase();
return (this.query.trim().toLowerCase() == symbol.substring(0, 1) ||
comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1);
},
highlighter: function (item) { // how to show the data
comp = me.map[item];
if (typeof comp === 'undefined') {
return "<span>No Match Found.</span>";
}
if (comp.t == 0) {
imgurl = comp.v;
} else if (comp.t == -1) {
imgurl = me.format(imgurl, "empty");
} else {
imgurl = me.format(imgurl, comp.t);
}
return "\n<span id='compVenue'>" + imgurl + "</span>" +
"\n<span id='compSymbol'><b>" + comp.s + "</b></span>" +
"\n<span id='compName'>" + comp.c + "</span>";
},
sorter: function (items) { // sort our results
if (items.length == 0) {
items.push(Object());
}
return items;
},
// the problem starts here when i start using target inside the functions
updater: function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
};
$("#" + target.inputId).typeahead(options);
// lastly, set up the functions for the buttons
$("#" + target.buttonId).click(function () {
window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination);
});
}
}
};
With #cdhowie's help, some more code:
i will update the updater and also the href for the click()
updater: (function (inner_target) { // what to do when item is selected
return function (item) {
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}}(target))};
I liked the paragraph Closures Inside Loops from Javascript Garden
It explains three ways of doing it.
The wrong way of using a closure inside a loop
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Solution 1 with anonymous wrapper
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Solution 2 - returning a function from a closure
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
Solution 3, my favorite, where I think I finally understood bind - yaay! bind FTW!
for(var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}
I highly recommend Javascript garden - it showed me this and many more Javascript quirks (and made me like JS even more).
p.s. if your brain didn't melt you haven't had enough Javascript that day.
You need to nest two functions here, creating a new closure that captures the value of the variable (instead of the variable itself) at the moment the closure is created. You can do this using arguments to an immediately-invoked outer function. Replace this expression:
function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
With this:
(function (inner_target) {
return function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}
}(target))
Note that we pass target into the outer function, which becomes the argument inner_target, effectively capturing the value of target at the moment the outer function is called. The outer function returns an inner function, which uses inner_target instead of target, and inner_target will not change.
(Note that you can rename inner_target to target and you will be okay -- the closest target will be used, which would be the function parameter. However, having two variables with the same name in such a tight scope could be very confusing and so I have named them differently in my example so that you can see what's going on.)
In ecmascript 6 we have new opportunities.
The let statement declares a block scope local variable, optionally initializing it to a value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Since the only scoping that JavaScript has is function scope, you can simply move the closure to an external function, outside of the scope you're in.
Just to clarify on #BogdanRuzhitskiy answer (as I couldn't figure out how to add the code in a comment), the idea with using let is to create a local variable inside the for block:
for(var i = 0; i < 10; i++) {
let captureI = i;
setTimeout(function() {
console.log(captureI);
}, 1000);
}
This will work in pretty much any modern browser except IE11.

TypeError: base.contacts is undefined - how can i define it?

Receiving the above error in reference to the line for ( var i = 0; i < base.contacts.length; i++) { and can't figure out how to resolve. Below is the relevant code section
synckolab.addressbookTools.createTBirdObject = function (base, cards) {
var card = null;
if (base.type === "contact") {
card = Components.classes["#mozilla.org/addressbook/cardproperty;1"].createInstance(Components.interfaces.nsIAbCard);
} else if (base.type === "maillist") {
card = Components.classes["#mozilla.org/addressbook/directoryproperty;1"].createInstance(Components.interfaces.nsIAbDirectory);
card.isMailList = true;
} else {
return null;
}
// for a mailing list add the entries
if (base.type === "maillist") {
card.dirName = this.getCardProperty(base, "DisplayName");
if (this.haveCardProperty(base, "NickName")) {
card.listNickName = this.getCardProperty(base, "NickName");
}
if (this.haveCardProperty(base, "Notes")) {
card.description = this.getCardProperty(base, "Notes");
}
// fill the list
for ( var i = 0; i < base.contacts.length; i++) {
var listCard = cards.get(this.getUID(base.contacts[i]));
card.addressLists.appendElement(listCard, false);
}
} else {
// go through all elements of base
for ( var field in base) {
// skip our own stuff TODO: handle mailing lists!
if (field !== "type" && field !== "synckolab" && field !== "ts" && field !== "contacts" && field !== "isMailList") {
// copy the property from base to card
this.setCardProperty(card, field, this.getCardProperty(base, field));
}
}
}
return card;
};
base is a parameter you hand over to the function which is supposed to be an object having (at least) the following properties:
type
contact
Obviously it is missing the contact property, which again should be an array. So you should take a look at the object which you are handing over to the function.
To fix this problem temporarily you could do:
if (base.contacts){
for ( var i = 0; i < base.contacts.length; i++) {
/*...*/
}
}
However to really fix the problem, you should take a look at the code, where the function is called and examine, how the object which you hand to the function is constructed. Do a
console.log(base);
to take a closer look at the object.

Categories