JavaScript DOM Dependent on Viewport Causing Strange Results - javascript

I am trying to find occurrences of a string in another string that has been pulled from the HTML document. The page is an SNMP monitor but we have been having issues in the past with CTRL + F because it only wants to find the string within the current viewport of the browser. My attempt at getting around this and not having to look through things manually was to write a script.
The issue here is that it appears the docHTML variable is only able to hold so much data and anything else is truncated. I have looked around on Stack Overflow and found that my string size is significantly less than other people have tried, so that shouldn't be the issue.
All of the IP addresses in the 'ipArray' variable do exist on the page in different locations and are in the docHTML variable when I look through it myself. When I run the doSearch function at various points in the page (viewport dependent) it gives me different results.
I really don't know what has gone wrong here as the code does work sometimes, and not other times. My goal is to have the code go through the whole page and find all missing IP's and add them to the array so that we can go ahead and add them instead of having to compare 490 IP's on a spreadsheet to up to 490 in the monitoring utility.
Thanks in advance!
var docHTML = document.documentElement.outerHTML;
var missing = [];
function doSearch(text) {
if (docHTML.search(text) == -1){
missing.push(text);
}
}
var ipArray = [
"192.168.64.236",
"192.168.64.237",
"192.168.64.238",
"192.168.64.10",
"192.168.64.11",
"192.168.64.12",
"192.168.65.40",
"192.168.65.47"
];
var Total = ipArray.length;
for(i=0;i<Total;i++){
doSearch(ipArray[i]);
}
console.log("Missing IP's: " + (Total - missing.length));
console.log(missing);

Here is the solution, not much of change, just a tweak to your logging statement. You were printing "total-missing" which is wrong. What we need is the missing count-
var docHTML = document.documentElement.outerHTML;
var missing = [];
function doSearch(text) {
if (docHTML.search(text) == -1){
missing.push(text);
}
}
var ipArray = [
"69.171.224.11",
"199.59.149.230",
"174.121.194.34",
"209.200.154.225",
"69.174.244.50",
"67.201.54.151"
];
var Total = ipArray.length;
console.log(Total);
for(i=0;i<Total;i++){
doSearch(ipArray[i]);
}
console.log("Missing IP's: " + (missing.length)); /***HERE***/
console.log(missing);
Other than this, the whole code worked for me as expected. Let me know what else/exactly is the issue. Happy to help.

The code works as intended. The issue happened to be the SNMP monitor it is running on top of. Everything on the page seems to be loaded by POST requests as you scroll. It seems to grab a few before and after which was why I was able to see it in the code and not when executing.

Related

Is it possible to get a random sentence from a Google Sheet and showing it on my website when someone click a button?

I am at starting web dev, already using html/css.
For a little project, I had a look at JavaScript. (My goal is that when people click a button, the site will show a random sentence that will be taken from a google sheet cell.)
Could you tell me please if it is even possible? If so, please share some ideas that I will explore. If not, please give me some alternative ideas... Thanks so much.
Have a good day!
-LeganV9
This is possible using Google Apps Script!
I have a working demo here, with the source being here. I dare you to get the jackpot. :D
In order to make this, you can go to https://script.new. Now, in code.gs put this:
function doGet() {
return HtmlService.createTemplateFromFile("index").evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getVals(){
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1IDbhQhaImcQB-4j-iByajwAkvxkutptcPMhMTxNrPtU/edit#gid=0");//Put your URL here
var sheet = ss.getSheetByName("Sheet1");//Put your sheet name here
var AMOUNT_OF_SENTENCES = sheet.getMaxRows().toString().replace(".0","");//You can replace this with a number eg 20
var range = sheet.getRange(1, 1,AMOUNT_OF_SENTENCES);
var values = range.getValues();
var newValues = [];
for(var i = 1; i<values.length;i++){
if(values[i][0] === "" || values[i][0] === " "){
}else{
newValues.push(values[i][0]);
}
}
return {valuesVar: newValues };
}
After that, create a new HTML file called "index" and put this in it:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>
The results are: <span id = "results">Loading...</span>
</h1>
<button id = "yourIdHere">Click me!!</button>
<script>
var yourDataList;
function onSuccess(data) {
yourDataList= data.valuesVar;
}
google.script.run.withSuccessHandler(onSuccess).getVals();
var myBtn = document.querySelector("#yourIdHere"); //Declare button, replace yourIdHere with your ID
var randomNum = 0; //Decclre random number
function getRandom() { //Declare your function
randomNum = parseInt(Math.random() * yourDataList.length); //Get random number between 0 and the length of your datalist
return yourDataList[randomNum]; //Return your value
}
myBtn.addEventListener("click", function() { //Bind a listener to the button
document.querySelector("#results").innerText = getRandom(); //Do whatever you want to with the random value
});
document.querySelector("#results").innerText = getRandom();//Change from loading...
</script>
</body>
</html>
Welcome to the world of web development! Hope your project is a success.
It should definitely be possible, since Google Sheets offers an API which has read/write functionality (https://developers.google.com/sheets/api).
You could even later extend this so people can submit their own sentences, given that writing to a Google Sheet is also possible with this API.
However, since you're starting out, consider treating this as an iterative process. You don't have to publish your first version, but just to prevent overwhelming yourself, you might want to set small milestones along the way - each adding more functionality. For example:
Create an array of random sentences (you could, for example, start with using this to keep it simple: https://github.com/JamesFT/Database-Quotes-JSON).
Select and log a random sentence to the console (console.log()) each time the script is executed.
Transfer the random sentence to render in HTML and allow a new sentence to be generated each time a button is pressed.
Move your sentences into a Google Sheet and begin exploring the API.
This way, you achieve something in a much shorter space of time, while working towards your end goal. It's a good way to keep motivated and make things more manageable.
Best of luck!

Apps Script extremely slow or endlessly "Preparing for execution..."

I have written a very simple code on my Google Sheets file. This is the purpose:
Save some cells values from StaticSheet (all the Copyxxx) that need to be copied in DynamicSheet.
Get the value of one specific cell inserted by the user manually.
Enter a While loop useful only to increase an indicator and get the number of the row where I want to copy those values previously saved.
Copy those values on this row but different columns.
The problem is that it seems that most of the time it does not even run the script after I told it to do so.
What is funny is that sometimes it works, super slowly, but it works for like a couple of minutes. And after it stops working again.
Could you please tell me what am I missing here please?
function Copy_Static_on_Dynamic() {
var app = SpreadsheetApp;
var ss = app.openById("xxxxyy--------yyzzzz")
var StaticSheet = ss.getSheetByName("DEAT Price");
var DynamicSheet = ss.getSheetByName("DEAT Price + TEST");
var CopySKU = StaticSheet.getRange(5,1,40);
var CopyPrices = StaticSheet.getRange(5,3,40,4);
var CopyUsage = StaticSheet.getRange(5,8,40);
var Week_1 = StaticSheet.getRange(2,4).getValues();
var i = 1;
Logger.clear();
while(DynamicSheet.getRange(i,3).getValues() != Week_1)
{
Logger.log(i);
i+=1;
}
CopySKU.copyTo(DynamicSheet.getRange(i,4,40));
CopyPrices.copyTo(DynamicSheet.getRange(i,6,40,4));
CopyUsage.copyTo(DynamicSheet.getRange(i,11,40));
}
If you see the "Preparing for Execution" message in the Apps Script editor, you can reload the browser window and run the function again. The program will likely go away.
So I think I have solved it.
As Serge insas was saying I had my script running on the background, I found it out on the "Execution" section, where you can also interrupt them.
After I discover it I kept testing, and I saw that the while loop needed almost 2 seconds to check the condition every time, making the script incredibly long.
So instead of:
while(DynamicSheet.getRange(i,3).getValues() != Week_1)
... I have created a variable declared previously such as:
var WeekLOOP = DynamicSheet.getRange(i,3).getValues();
while(WeekLOOP != Week_1) { --- }
... and now the script needs few milliseconds to run the condition. I don't have enough technical knowledge to say if this was the only issue, but is what apparently solved my problem.
Thanks to all for the support! Will come back if I need any further help :)
As was mentioned by Amit Agarwal, to solve the error message mentioned on the question, refresh the web browser tab.
Regarding the code,
On
var Week_1 = StaticSheet.getRange(2,4).getValues();
and
DynamicSheet.getRange(i,3).getValues()
instead of getValues you should use getValue because your code are referring to single cell cells otherwise you will be getting 2D arrays instead of scalar values.
The use of while should be made very carefully to avoid functions running endlessly. You could add some "safeguard" like the following
var max_iterations = 100 // Edit this
while(DynamicSheet.getRange(i,3).getValue() != Week_1 && i <= max_iterations) {

Store data from an array and use it later

I had this problem which Cooper helped me to solve it (thanks again for that), but now I'm struggling with a different one. The following script will count how many times a client code will appear on another Spreadsheet using as a second condition yesterday date.
function countSheets()
{
var vA = appSh();
var td = Utilities.formatDate(subDaysFromDate(new Date(),2), Session.getScriptTimeZone(), "dd/MM/yyyy");
var mbs=getAllSheets();
//var s='';
for (var i=2;i<vA.length;i++)
{
var d = Utilities.formatDate(new Date(vA[i][12]), Session.getScriptTimeZone(), "dd/MM/yyyy");
for(var key in mbs)
{
if(vA[i][0]==key && d==td)
{
mbs[key]+=1;
}
}
}
return mbs;
}
Then I have the below code which will search in the main spreadsheet (a table) a string and when was found will return row number, also will search for the date yesterday and return the column number. Based on these information I'll get the range where I need to paste the count result from the first script.
function runScript()
{
var ss=SpreadsheetApp.openById('ID');
var mbs=countSheets();
for(var key in mbs)
{
var sh=ss.getSheetByName(key);
var rg=sh.getDataRange();
var vA=rg.getValues();
for(var i=0;i<vA.length;i++)
{
if(vA[i][1]=='Total Number of Applications')
{
var nr=i;
break;//by terminating as soon as we find a match we should get improved performance. Which is something you cant do in a map.
}
}
if(typeof(nr)!='undefined')//If we don't find a match this is undefined
{
var today=subDaysFromDate(new Date(),2).setHours(0,0,0,0);
for(var i=0;i<vA[3].length;i++)
{
if(vA[3][i])//Some cells in this range have no contents
{
if(today.valueOf()==new Date(vA[3][i]).valueOf())
{
sh.getRange(nr+1,i+1,1,1).setValue(Number(mbs[key]));
}
}
}
}
}
return sh;
}
PROBLEM: I have 24 rows on the main Spreadsheet. So I will need to write the same script 24 times. As example, I need to count Total Number of Applications, Total Number of Calls, Number of Live Adverts and so on. If I do this it will exceed execution time since each script takes on average 25 seconds to run.
I did some researches on this website and internet and read about storing values and re-use them over and over. At the moment my script will have to go every time through the same file and count for each condition.
Q1: Is there any chance to create another array that contain all those strings from the second script?
Q2: How to use PropertiesService or anything else to store data and don't have to run over and over getValues() ? I've read Google Documentation but couldn't understand that much from it.
I hope it all make sense and can fix this problem.
My best regards,
Thank you!
My Approach to your Problem
You probably should write it for a couple of rows and then look at the two of them and see what is unique to each one. What is unique about each one is what you have to figure out how to store or access via an external function call.
The issue of time may require that you run these functions separately. I have a dialog which I use to load databases which does exactly that. It loads 800 lines and waits for 10 seconds then loads another 800 lines and wait for ten seconds and keeps doing that until there are no more lines. True it takes about 10 minutes to do this but I can be doing something else while it's working so I don't really care how long it takes. I do care about minimizing my impact to the Google Server though and so I don't run something like this just for fun.
By the way the 10 second delay is external to the gs function.

Can't assign querySelectorAll() to a variable - weird behaviour

I was trying to crawl a very old website for a specific tag, I need to get it by it's for= attribute. So I used this piece of code.
var character = document.querySelectorAll("label[for=char_1]");
For some reason it returns an undefined, but I was using this script for a few days now and it worked like a charm. Here's the fun part. Typing that command in browsers console will result in undefined. But typing this alone:
document.querySelectorAll("label[for=char_1]");
Will return a proper NodeList. Why it won't assign to a variable...?
edit: It seems that deleting var and typing character without it will make it work. It's resolved but I would still love to get an answer to "why is this happening"?
edit2:
for (var i = 0; i < 15; i++) {
var character = document.querySelectorAll("label[for=char_" + i +"]");
console.log(character); // this will return [] from the script.
var color = character[0].children[0].style.color;
}
A simple for loop. All I get is Cannot read property 'children' of undefined. But I can type in the very same command document.querySelectorAll... and it will work in the browser and will return NodeList.
I had it working like this in a very hacky script. It worked.
var character1 = document.querySelectorAll("label[for=char_1]");
var characterColor1 = character1[0].children[0].style.color;
edit3:
var character1 = document.querySelectorAll("label[for=char_1]");
var characterColor1 = character1[0].children[0].style.color;
var character2 = document.querySelectorAll("label[for=char_2]");
var characterColor2 = character2[0].children[0].style.color;
// ...
The above code works without a single problem though. I don't think it's DOM not being ready as this code is also run from Greasemonkey script and it works. The only difference is the for loop.
var x = ""; // returns undefined, because it's a var assignment.
var elements = document.querySelectorAll('div');
That's expected behavior when pasted into the console.
edit: It seems that deleting var and typing character without it will make it work. It's resolved
I'm afraid you're creating a global scope variable now. or perhaps characters is an already defined variable in that scope.
Buhah, as I said in edit 3 "the only difference is the for loop". I was so busy trying to find an answer in the DOM-related things that I made the simplest mistake ever done in programming.
See?
char_1
With...
for(var i = 0...)
0! And I was testing char_1 in the browser instead of char_0. Which - truly - returns [] instead of something useful. Time to go on a holiday break I guess, my brain seems to be there already. :)

Password Strength Meter

I'm trying to create my own JS Password Strength Meter.
It was working before but i didn't like how it worked so I tried using
{score +=10;}
Instead of just:
score++
This is my code:
http://jsfiddle.net/RSq4L/
Best Regards,
Shawn,
Hope someone can help
Multiple issues:
Your passwordStrength() function was not defined in the global scope in the jsFiddle so it wasn't getting called. This is probably an artifact of how you set up the jsFiddle, perhaps not an issue in your real code.
The method of getting the appropriate ratingMsg will not work because you don't have array values for every possible score so many scores will generate an "undefined" ratingMsg.
Your CSS classes are also sparse so there are many score values that they will not match for either and no appropriate CSS class/style will be in effect. If you want a specific class for each rating value, then perhaps you should put the classname in the ratings array so it can be fetched from there along with the ratingsMsg.
For the first issue, in your jsFiddle, you also have to make sure the password processing function is defined in the global scope. The way your jsFiddle is set up, it is not (it's in the onload handler). You can fix this in the jsFiddle by just setting the first drop-down in the upper left to "no wrap (head)".
For the second issue, you are using:
ratingMsg[score]
but, your array is a sparse array not guaranteed to have an entry for most possible scores. You simply can't do it that way because many elements you access will have undefined values which won't give you a meaningful message. For example, if score was 15, you would be accessing ratingMsg[15], but there is no value in that space in the array so you won't get a meaningful rating message.
The solution is to find a different way to select the right message. The simplest way would just be an if/else if/else if statement that would check which range the score is in and set the appropriate msg. There are more elegant table driven ways, but all will involve searching through a data structure to find which two values the current score is between and using that msg.
If you look at this jsFiddle http://jsfiddle.net/jfriend00/dA7XC/, you'll see that your code is getting called, but it only hits values in the array sometimes.
And, here's a rewritten algorithm that finds the appropriate msg no matter what the score show in this fiddle: http://jsfiddle.net/jfriend00/jYcBT/.
It uses a data structure like this:
var ratingMsg = [
0, "Unclassified",
10, "Weak",
20, "Fair",
50, "Better",
60, "Medium",
70, "Good",
90, "Strong"
];
and a for loop like this to get the appropraite ratingMsg:
for (var i = ratingMsg.length - 2 ; i >= 0; i-=2) {
if (score >= ratingMsg[i]) {
msg = ratingMsg[i+1];
break;
}
}
Here you go: http://jsfiddle.net/RSq4L/11/
The first problem is that in your fiddle you have the onLoad option set, so your passwordStrength function is not actually being declared in the global scope. It is being declared inside of the onLoad block that jsFiddle wraps your code with. This causes the page to error out when the keypress handler tries to invoke the function.
You can fix this problem in several different ways:
By explicitly declaring the function as global as per my example above.
By choosing one of jsFiddle's "no wrap" options instead of onLoad.
By dynamically binding your event-handler instead of setting it through the element's onkeydown attribute in the markup.
The second problem is how you are keying your score messages. You have:
var ratingMsg = new Array(0);
ratingMsg[0] = "Unclassified";
ratingMsg[10] = "Weak";
ratingMsg[30] = "Fair";
ratingMsg[50] = "Better";
ratingMsg[60] = "Medium";
ratingMsg[70] = "Good";
ratingMsg[90] = "Strong";
...and you lookup the message by doing ratingMsg[score]. This will only work if the score exactly matches one of your indices. And based upon your math this will not always be the case.
I would suggest doing something like:
ratingMsg = {};
ratingMsg[0] = "Unclassified";
ratingMsg[10] = "Weak";
ratingMsg[30] = "Fair";
ratingMsg[50] = "Better";
ratingMsg[60] = "Medium";
ratingMsg[70] = "Good";
ratingMsg[90] = "Strong";
function closestRating(score) {
var bestKey = 0;
var bestMatch = 100;
for (var key in ratingMsg) {
if (key <= score && score - key < bestMatch) {
bestMatch = score - key;
bestKey = key;
}
}
return ratingMsg[bestKey];
}
On an unrelated note, are you sure you want to be using onkeydown? I think onkeyup would work better.
Your fiddler script had several errors. Here's the corrected one: new script.
You were missing a semicolon here: document.getElementById("passwordDescription").innerHTML = "" + ratingMsg[score] + ""
You forgot to escape '^' on your regular expression
I just wrote this for it:
Jquery Plugin for password strength forcing

Categories