Print JavaScript condition used in Shiny conditionalPanel - javascript

I'm creating a conditionalPanel in a Shiny app. I'm attempting to debug the JavaScript condition, but without inspecting it, I'm just guessing random JavaScript bits. Is there a way to inspect the condition directly?
selectizeInput('groups','Groups:',
choices = list('Choice1' = 'choice1','Choice2' = 'choice2'),
multiple = TRUE,selected = NULL
)
conditionalPanel(
print("input.groups.indexOf('choice2') >= 0"), # desired output
condition = "input.groups.indexOf('choice2') >= 0",
selectInput("statusfilter", "StatusFilter",
list("NewChoice1" = "nc1","NewChoice2" = "nc2"))
)
The above code (generalized from my production code) is showing the conditionalPanel at start up before I even click a choice. Once I do, it will disappear until I choose 'choice2'. If I could inspect the condition, I could fix this quickly.

After commenting our the print statement so that the code runs, if you open your javascript console, you'll see an error:
Error parsing expression: input.groups.indexOf('choice2') >= 0
This suggests your JS code breaks. This is because input.groups is null , and doing .indexOf on a null value causes an error.
So what you want in the condition is:
condition = "input.groups !== null && input.groups.indexOf('choice2') >= 0"

Related

Javascript RegEx causes message loop and crashes browser when set to anything other than /mg

I've created a script (my first) that accepts input text and then runs about 30 regular expressions on the text, outputting discovered values into an HTML table. The purpose is for me to be able to paste in text containing specification information for products and have them uniformly outputted so I can paste them into a spreadsheet.
I've had it working really well and I've been tuning the regexes as I've pasted data with different variations/layouts in. However, I've hit an impasse and need some assistance:
One of the regular expressions searches for the product part number (sku) and returns the value in a column. Some of the source data includes more than one match because there are regional variations to the products. In all cases the first match is the only one that I want. I've tested the RegEx on RegEx101 and it returns the first match only with the 'global' flag switched off. However, the same RegEx running in my script causes it to return console messages infinitely before crashing. It's immediately unresponsive so I can't see any error messages.
Here's a sample of the regex section in my script. sku being the one that's causing problems:
let wallMountable = /(?<wallmountable>[Ww]all [Mm]ount)/mg;
let sku = /^.*\((?<sku>\d\d\w\w[\d\w]+?).+?\).*$/m;
function parseData() {
// Execute the Regular Expressions on the text in 'specSheet'
let specSheet = document.getElementById("specSheet").value;
let wallMountableToken = wallMountable.exec(specSheet);
let skuToken = sku.exec(specSheet);
do {
// If Token isn't null, then test to see if the regex group value is undefined. If either are true, do nothing, otherwise write the value to the document.
if (wallMountableToken !== null) {
if (wallMountableToken.groups.wallmountable !== undefined)
{document.write(`${wallMountableToken.groups.wallmountable}`);}
else {}
}
else {}
if (skuToken !== null) {
if (skuToken.groups.sku !== undefined)
{document.write(`${skuToken.groups.sku}`);}
else {}
}
else {}
}
// Loop through the script until a condition is met.
while (
(wallMountableToken = wallMountable.exec(specSheet)) !== null,
(skuToken = sku.exec(specSheet)) !== null
);
}
The while loop may not be necessary and in truth I'm not entirely sure what purpose it serves here, but it seemed to consistently appear in the reference material I was studying. I have included it here because it's part of the script, but please note that the script works if I change the second regex to /mg instead of /m, however, it returns multiple values and I only want it to return the first capture.
I know there's a lot wrong with the script, but this particular question is about why the regex is causing an infinite loop, so any help toward that goal is much appreciated.

Javascript internally changing array values, any fix?

I'm making a chess game. And I'm storing some tiles of the chessboard for some usage in my program in an array. The problem is, when I update the values in the array, and log them onto the console, I can see the values of the array, which are correct. But, upon clicking and checking the actual values in the console, the array shows different values.
I'm using google chrome's console.
I've already seen another stack overflow saying that this is a known "bug", but the developers won't fix it because it's not actually a bug.
Here's what I see in the console:
(2) ["a6", "a5"]
0: "`6"
1: "b6"
length: 2
__proto__: Array(0)
As you see, the values in the array I see are different than the values that are stored in the array. And this causes many other problems in my program which rely on the contents of this array.
What I want to know is if this is common, and also if there is any fix. Or maybe some scenarios where cases like this occur.
Also, I'm still unsure, for the array shown above ^, are the actual values of the array the values I see ("a6" and "a5"), or the wrong values shown internally ("`6" and "b6").
Please help, if this doesn't work, it will be extremely difficult to finish the project I'm working on.
Here is the code I use to update the array:
let moves = [];
if (tile.slice(1, 2) == 7 && b1.board[dd].piece == null && b1.board[d].piece == null) {
moves.push(d);
moves.push(dd);
}
if (tile.slice(1, 2) == 7 && b1.board[d].piece == null && b1.board[dd].piece != null) {
moves.push(d);
}
if (parseInt(tile.slice(1, 2)) > 1 && tile.slice(1, 2) != 7 && b1.board[d].piece == null) {
moves.push(d);
}
if (tile.charCodeAt(0) > 97 && parseInt(tile.slice(1, 2)) > 1 && b1.board[ld].piece != null && b1.board[ld].piece.slice(0, 1) == "w") {
moves.push(ld);
}
if (tile.charCodeAt(0) < 104 && parseInt(tile.slice(1, 2)) > 1 && b1.board[rd].piece != null && b1.board[rd].piece.slice(0, 1) == "w") {
moves.push(rd);
}
console.log(moves);
just to provide some context, I make an array called moves. I push values into the array based on some conditions. Then I log the array onto the console, which is what I showed above.
The if statements are working because the values I see in the array are correct. But as you know, upon clicking and checking the internal values in the console, they are wrong.
The result I am expecting from the console is:
(2) ["a6", "a5"]
0: "a6"
1: "a5"
length: 2
__proto__: Array(0)
How do I fix this problem?
Not sure this is the cause, but when console.log, Chrome and other browser don't output the serialized values of the arrays/objects but just stores the reference on them. Later when you inspect these arrays/objects Chrome will show the current values in those arrays, not the values that were present at the time of logging.
Try logging with console.log(JSON.stringify(thingToLog)). Or even better with console.log(JSON.stringify(thingToLog, undefined, ' ')).

Vue JS - indexOf array is not a function

I'm trying to check if a number is present in an array (which I've done a thousand times before using .indexOf()) but I seem to be missing something now.
Vue method
showSeat(seat) {
if( !this.selectedSeats.length ) {
this.selectedSeats.push(seat)
} else {
let index = this.selectedSeats.indexOf(seat)
( index >= 0 ) ? this.selectedSeats.splice(index,1) : this.selectedSeats.push(seat)
}
}
Initially, this.selectedSeats is equal to [], and the first condition runs perfectly. However, when I try to add another seat, I get [Vue warn]: Error in event handler for "showSeat": "TypeError: this.selectedSeats.indexOf(...) is not a function". What am I missing?
This is one of those rare cases in JavaScript where leaving off a semicolon can cause huge problems. These two lines are being evaluated as a single expression:
let index = this.selectedSeats.indexOf(seat)
( index >= 0 ) ? this.selectedSeats.splice(index,1) : this.selectedSeats.push(seat)
It's trying to execute this.selectedSeats.indexOf(seat)(index>=0)
Add a semicolon at the end of your indexOf(seat); and you should be fine.

React .trim() is not a function

My application has a landing page with two components in two separate tabs.
The code from the first component that is causing the crash looks like this:
for (let key in linegraphdata) {
linegraphdata[key].price = Number(
linegraphdata[key].price.trim().slice(1)
);
linegraphdata[key].month = parseDate(linegraphdata[key].month);
}
When I load into my application initially it doesn't crash, loads the data from the first tab fine. I'll click into the second tab and when I eventually click back the whole application crashes and the log gives me this error:
Uncaught TypeError: linegraphdata[key].price.trim is not a function
It must have something to do with how React handles refreshing components once already rendered, could anyone help me figure it out please :)
You're setting what was a string to a number, and numbers don't have the trim() method on them. That's why it works the first time (when it's a string) and not the second time around:
array[key] = Number(array[key].trim());
So that code must be executing more than once.
linegraphdata[key].price is either null or not a string.
If there is a value, you can try using linegraphdata[key].price.toString().trim().slice(1)
You can check that price is a string with this ternary. If it's not a string it will set the value to -1
linegraphdata[key].price = Number(
typeof linegraphdata[key].price == 'string' ? linegraphdata[key].price.trim().slice(1) : -1
);

Unchecking and simulating a click on checkboxes in Javascript

I am admittedly a super newbie to programming in general. I am trying to design a quick piece of javascript to inject on a website for a class that will both uncheck and simulate a click on a series of checkboxes. This is nothing malicious, the web form we use to download data for use in this class presents way more variables than necessary, and it would be a lot more convenient if we could 'uncheck' all and only check the ones we want. However, simply unchecking the boxes via javascript injection doesn't yield the desired result. A mouse click must be simulated on each box. I have been trying to use the .click() function to no avail. Any help is greatly appreciated. My code below fails with an error of:
"TypeError: Cannot read property 'click' of null"
CODE:
var getInputs = document.getElementsByTagName("input");
for (var i = 0, max = getInputs.length; i < max; i++){
if (getInputs[i].type === 'checkbox')
getInputs[i].checked = false;
document.getElementById('shr_SUBJECT=VC' + i).click();
}
--------EDIT#1--------------
FYI, this is the website that I am trying to use this on:
http://factfinder2.census.gov/faces/nav/jsf/pages/searchresults.xhtml
if you search for and open up any of these tables they are huge. It would be awesome if I could easily pare down the variables by 'unchecking' and 'clicking' them all at once via javascript.
The code at the bottom ALMOST works.
The problem I am running into now is that it throws an error after the first or second run through the for loop:
"TypeError: document.getElementById(...) is null"
I understand that this is because the value it's trying to find doesn't exist? Sometimes on these tables the checkboxes are greyed out/don't exist or are otherwise 'unclickable'. My theory as to why I am getting this error is because in the table/form the 'available' ID's will start around:
shr_SUBJECT=VC03 or sh_SUBJECT=VC04
and it may then skip to:
shr_SUBJECT=VC06 then skip to shr_SUBJECT=VC09 and so on...
So if the for loop hits an ID that isn't available such as 05 or 07, it returns a null error :(
I did some reading and learned that javascript is able to 'catch' errors that are 'thrown' at it? My question now is that I'm wondering if there is an easy way to simply iterate to the next ID in line if this error is thrown.
Again, any and all help is appreciated, you guys are awesome.
OLD DRAFT OF SCRIPT
var getInputs = document.getElementsByTagName("input");
for (var i = 3, max = getInputs.length; i < max; i++){
if (getInputs[i].type === 'checkbox' && i < 10){
var count = i;
var endid = count.toString();
var begid = "shr_SUBJECT=VC0";
var fullid = begid.concat(endid);
document.getElementById(fullid).click();
}
else if(getInputs[i].type === 'checkbox' && i >= 10){
var count = i ;
var endid = count.toString();
var begid = "shr_SUBJECT=VC";
var fullid = begid.concat(endid);
document.getElementById(fullid).click();
}
}
--------EDIT#2----------
An example of a table that I am trying to manipulate can be found at this URL:
http://factfinder2.census.gov/faces/tableservices/jsf/pages/productview.xhtml?pid=ACS_12_5YR_DP02&prodType=table#
If you click on the 'Modify Table' button, you are able to select/deselect specific variables via the checkboxes. If you right-click on a couple of 'active' checkboxes and inspect the elements, and it looks something like this:
<input id="shr_SUBJECT=VC03" checked="" alt="hide SUBJECT=VC03" name="" value="" onclick="javascript:hiderow('SUBJECT=VC03');" type="checkbox">
<input id="shr_SUBJECT=VC25" checked="" alt="hide SUBJECT=VC25" name="" value="" onclick="javascript:hiderow('SUBJECT=VC25');" type="checkbox">
Thank you so much #Jonathan Steinbeck for the tip about the ternary operator, it really cleaned up my code.
The script works properly, but the problem I am running into now is that it doesn't iterate enough times after the try, catch statement. If there is a gap in the id #'s; say it jumps from shr_SUBJECT=VC19 to shr_SUBJECT=VC=24 the script will stop running. Is there a way to make it keep retrying the try/catch until it gets a valid ID # or one that exists/is an active checkbox?
CURRENT DRAFT OF SCRIPT :
var getInputs = document.getElementsByTagName("input");
for (var i = 3, max = getInputs.length; i < max; i += 1) {
try {
if (getInputs[i].type === 'checkbox'){
document.getElementById("shr_SUBJECT=VC" + (i < 10 ? "0" : "") + i).click();
}
}
catch (err) {
i+=1;
if (getInputs[i].type === 'checkbox'){
if (getInputs[i].type === 'checkbox'){
document.getElementById("shr_SUBJECT=VC" + (i < 10 ? "0" : "") + i).click();
}
}
}
}
When you call document.getElementById() with a non-existing ID, null is returned. Therefore this error means that you're trying to call the .click() method on null, which can't work.
So you should check what the correct ID naming scheme for the elements you want is. Maybe the elements' count starts with 1 instead of 0?
Also, the .click() doesn't work for all elements like you would expect as far as I know. So depending on the kind of element you are trying to retrieve you might have to create and dispatch your own event as suggested by RobG's comment.
EDIT in response to your recent edit:
You can wrap code that throws errors in a try-catch like this:
for (var i = 3, max = getInputs.length; i < max; i += 1) {
try {
document.getElementById("the_ID").click();
}
catch (error) {
console.error(error);
// continue stops the current execution of the loop body and continues
// with the next iteration step
continue;
}
// any code here will only be executed if there's not been an error thrown
// in the try block because of the continue in the catch block
}
Also, what are you doing with the 'i' variable? It doesn't make sense to assign it to so many variables. This does the same:
document.getElementById("shr_SUBJECT=VC" + (i < 10 ? "0" : "") + i).click();
The ... ? ... : ... is an operator (called the 'ternary operator') that works like this: evaluate the expression before the "?" - if it results in a truthy value, the expression between "?" and ":" is evaluated and becomes the result of using the operator; if the condition results to false, the part after the ":" is evaluated as the value of the operator instead. So while "if" is a statement in JavaScript (and statements usually don't result in a value), the ternary operator can be used as an expression because it results in a value.
By concatenating a string with something else, you are forcing the 'something else' to be converted to string. So an expression like this will usually result in a string:
"" + someNonStringVar
Also, it doesn't make sense to define variables in a loop body in JavaScript. JavaScript variables have function scope, not block scope. What this means is that any variables defined in the loop body exist inside the whole function as well. Therefore it is recommended to write all of the "var"s at the top of your function to make it clear what their scope is. This behaviour of JavaScript is called 'hoisting', by the way.
I've furthermore taken a look at the URL you've given in your recent edit but I fail to find the kind of naming scheme for IDs you describe. In which table did you find those?
Edit in response to your second edit:
You shouldn't mess with the 'i' variable inside the body of a for loop. It makes your code much harder to reason about and is probably not what you want to do anyway. You don't need to handle the next step of the iteration in the catch block. The 'i' variable is incremented even if there's an error during fetching the element from the DOM. That's why you use catch in the first place.

Categories