Unexpected infinite loop in Javascript - javascript

I was doing a cookie practice in javascript and developed the following code:
function getCookie(kname) {
var fullkeyname = kname + "=";
var fullcookie = document.cookie;
var acookies = fullcookie.split(";");
for (var i = 0; i < acookies.length; i++) {
var cookie = acookies[i];
while (acookies[i].charAt(0) == " ") { //THE ISSUE IS HERE
cookie = cookie.substring(1);
}
if(cookie.indexOf(fullkeyname) == 0) {
return cookie.substring(fullkeyname.length, cookie.length);
}
}
return "";
}
It didnt worked and seemed to rise and infinite loop so I tracked my code with "document.write" in various parts and discovered that if I change the "while" condition it will work as follows:
function getCookie(kname) {
var fullkeyname = kname + "=";
var fullcookie = document.cookie;
var acookies = fullcookie.split(";");
for (var i = 0; i < acookies.length; i++) {
var cookie = acookies[i];
while (cookie.charAt(0) == " ") { //RIGHT HERE
cookie = cookie.substring(1);
}
if (acookies[i].indexOf(fullkeyname) == 0) {
return cookie.substring(fullkeyname.length, cookie.length);
}
}
return "";
}
Now my code works as expected, however I dont know why, since I have set the value of cookies to "cookie=acookies[i]" I dont understand why it should loop indefinetly if I dont use the "cookie" variable.
Question is: why does the code works with my "cookie" variable and not with the "acookies[i]" even though the sould have the same value at that given moment?

It causes an infinite loop because you are checking if the first character of acookies is an empty space, then if it is, you are removing the first character of cookie (NOT acookies). Therefore, if acookies does have a space as the first character, the while statement will always be true because you aren't removing that space from acookies
function getCookie(kname) {
var fullkeyname = kname + "=";
var fullcookie = document.cookie;
var acookies = fullcookie.split(";");
for (var i = 0; i < acookies.length; i++) {
while (acookies[i].charAt(0) == " ") { //THE ISSUE IS HERE
acookies[i] = acookies[i].substring(1); // FIXED THIS LINE
}
var cookie = acookies[i];
if(cookie.indexOf(fullkeyname) == 0) {
return cookie.substring(fullkeyname.length, cookie.length);
}
}
return "";
}
edited for typo, thank you FREE_AND_OPEN_SOURCE

I see a problem in while loop
while (acookies[i].charAt(0) == " ") { //THE ISSUE IS HERE
cookie = cookie.substring(1);
}
once acookies[i].charAt(0) == " " becomes true, it will keep on looping and will never get to go outside while loop to increment the value of 'i'.
and in the second version of the code you're modifying the cookie variable itself which make the while condition false which moves the control out of the loop and continue execution.

Related

How to underline charachters in InDesign with JavaScript?

I started writing this piece of code for InDesign to underline all letters except from the one with descendants, and added a dialog window to chose stroke and offset of the line.
Now I have two problems:
the program underlines all letters
the stroke and offset won't change
I'm a beginner in Javascript and it's the first time coding for InDesign. Does someone have a clue? Thank you!
// UNDERLINE ALL BUT NO DESCENDANTS
//Make certain that user interaction (display of dialogs, etc.) is turned on.
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
if (app.documents.length != 0){
try {
// Run script with single undo if supported
if (parseFloat(app.version) < 6) {
main();
} else {
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Expand State Abbreviations");
}
// Global error reporting
} catch ( error ) {
alert( error + " (Line " + error.line + " in file " + error.fileName + ")");
}
}else{
alert("Open a document first before running this script.");
}
///MAIN FUNCTION
function main(){
if(app.selection.length != 0){
myDisplayDialog();
}
}
//INTERFACE
function myDisplayDialog(){
//declare variables
//general
var myDoc = app.activeDocument;
var mS = myDoc.selection;
// dialog
var myDialog = app.dialogs.add({name:"Underliner"});
var myLabelWidth = 70;
with(myDialog.dialogColumns.add()){
with(borderPanels.add()){
with(dialogColumns.add()){
with(dialogRows.add()){
staticTexts.add({staticLabel:"Stroke:", minWidth:myLabelWidth});
staticTexts.add({staticLabel:"Offset:", minWidth:myLabelWidth});
}
}
with(dialogRows.add()){
staticTexts.add({staticLabel:""});
var myStroke = measurementEditboxes.add({editValue:1, editUnits:MeasurementUnits.points});
var myOffset = measurementEditboxes.add({editValue: 15, editUnits:MeasurementUnits.points});
}
}
}
var myResult = myDialog.show();
if(myResult == true){
var myStroke = myStroke.editValue;
var myOffset = myOffset.editValue;
myDialog.destroy();
underline(mS,myStroke,myOffset);
}
else{
myDialog.destroy();
alert("Invalid page range.");
}
}
//REAL FUNCTION
function underline(charList,stroke, offset){
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'];
for (var i=0; i < len; i++){
try{
var myChar = charList[i];
//console.log(typeof myText);
if (includes(myChar, doNotUnderline) == false)
{
myChar.underline = true;
myChar.underlineWeight == stroke;
myChar.underlineOffset == offset;
} else {
myChar.underline = false;
}
}catch(r){
alert(r.description);
break;
}
}
}
//function to know if char is in array
function includes(elemento,array)
{
var len = array.length;
for(var i=0; i<len ;i++)
{
if(array[i]==elemento){return true;}
}
return false;
}
Try these changes in the function underline():
//REAL FUNCTION
function underline(words,stroke, offset) { // <------ here 'words' instead of 'charList'
var charList = words[0].characters; // <------ here get 'characters' of the 'words'
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'].join(); // <------- here '.join()'
for (var i=0; i < len; i++){
try{
var myChar = charList[i];
// if (includes(myChar, doNotUnderline) == false) // <----- no need
if (doNotUnderline.indexOf(myChar.contents) < 0) // <------ 'indexOf()' instead of 'includes()'
{
myChar.underline = true;
myChar.underlineWeight = stroke; // <------- here '=' instead of '=='
myChar.underlineOffset = offset; // <------- here '=' instead of '=='
} else {
myChar.underline = false;
}
}catch(r){
alert(r.description);
break;
}
}
}
Probably there can be another improvements as well. It's need additional researches. But if you change these lines it should work to a degree.
And there is one little thing that improves user experience greatly: to keep last used values in the input fields. It can be done pretty easy, let me know it you need it.
Update
Here is the way I'm using to store and restore any preferences of my scripts.
Add somewhere at the start of your script these lines:
// get preferences
var PREFS = { stroke: 1, offset: 15 }; // set default prefs
var PREFS_FILE = File(Folder.temp + '/underline_prefs.json'); // the file with preferences
if (PREFS_FILE.exists) PREFS = $.evalFile(PREFS_FILE); // get the prefs from the file
Now you can use the global values PREFS.stroke and PREFS.offset anywhere you want. In your case they go here:
with(dialogRows.add()){
staticTexts.add({staticLabel:""});
var myStroke = measurementEditboxes.add({editValue:PREFS.stroke, editUnits:MeasurementUnits.points});
var myOffset = measurementEditboxes.add({editValue:PREFS.offset, editUnits:MeasurementUnits.points});
}
This way script will get the stroke and weight from the file underline_prefs.json that will be stored in the standard temporary folder of current user.
Final step is to save the values back into the file after the script got them from the dialog window.
I'd put this piece of code here:
if (myResult == true) {
var myStroke = myStroke.editValue;
var myOffset = myOffset.editValue;
myDialog.destroy();
underline(mS, myStroke, myOffset);
// save preferences here
PREFS.stroke = myStroke;
PREFS.offset = myOffset;
PREFS_FILE.open('w');
PREFS_FILE.write(PREFS.toSource());
PREFS_FILE.close();
} else {
myDialog.destroy();
alert("Invalid page range.");
}
Voilá. Now don't need to type the values every time they differ from default ones.

Have a lot of variables that are in order. I need to convert them using JavaScript to contain something else but don't want to do it one by one

First of all I'm not a programmer. I need to use some really basic HTML, CSS and XML for my work. The program I am using allows running javascripts, too.
I usually have a lot of variables from my XML files. They go by something like this:
VAL001
VAL002
VAL003
VAL004
You get it.
These variables are often checkboxes. The values can be either 'Checked' or 'Unchecked'.
Instead of embedding these variables in the HTML code, I tend to convert it to something else so it gets nicer. Like this:
if ( VAL001 == 'Checked' ) CHK001 = '✓';
else CHK001 = '';
When this is done, I insert CHK001 (instead of VAL001) in the HTML so I get a nice check mark if the box was checked and nothing when it was not checked. When there are a lot of these boxes it's not too effective to do it one by one.
What I tried in JavaScript is:
var i;
for ( i = 1, i <= 9, i++ ) {
if ( VAL00$i == 'Checked' ) CHK00$i = '✓'
else CHK00$i = '';
}
var j;
for ( j = 10, j <= 99, j++ ) {
if ( VAL0$j == 'Checked' ) CHK0$j = '✓'
else CHK0$j = '';
}
I thought that this would replace the last digits with i and j and I would get what I need. Unfortunately this just brings up a ReferenceError saying that VAL00$i can't be found.
If I replace the $i and $j with [i] and [j] I get the same ReferenceError but this time i and j are not told to be wrong so I get that VAL00 can't be found. A simple solution would really speed up things for me. Thank you in advance!
You could put your variables in an array and use map to check and change the variables to be a tick or not.
var array = [
VAL001,
VAL002,
VAL003,
VAL004
];
var newArray = array.map(val=>{
if (val === 'Checked') return '✓';
else return '';
});
Alteratively, if you need to know the names of the variables after checking everything you could use an object.
var obj = {
VAL001: VAL001,
VAL002: VAL002,
VAL003: VAL003,
VAL004: VAL004
};
var newObj;
for (var i of Object.keys(obj){
if (obj[i] === 'Checked') newObj[i] = '✓';
else newObj[i] = '';
}
If you are having VAL001 variables as property in obj then you can perform like below.
Here i.toString().padStart(3, 0), for i = 1 it will return 001 similarly for i=10 it will return 010; You can access property of object with obj[propertyName]. So these values will be accessible with obj[VAL${index}].
var obj = {
VAL001: 'Checked',
VAL002: '',
VAL003: 'Checked',
VAL004: '',
VAL010: '',
VAL099: 'Checked',
};
var result = {};
for (var i = 1; i <= 99; i++) {
let index = i.toString().padStart(3, 0);
if (obj.hasOwnProperty(`VAL${index}`)) {
if (obj[`VAL${index}`] == 'Checked') result[`CHK${index}`] = '✓'
else result[`CHK${index}`] = '';
}
}
console.log(result);
If you are having variables in global scope then you can use windows["VAL001"].
var VAL001 = 'Checked',
VAL002 = '',
VAL003 = 'Checked',
VAL004 = '',
VAL010 = '',
VAL099 = 'Checked';
for (var i = 1; i <= 99; i++) {
let index = i.toString().padStart(3, 0);
if (window.hasOwnProperty(`VAL${index}`)) {
if (window[`VAL${index}`] == 'Checked') window[`CHK${index}`] = '✓'
else window[`CHK${index}`] = '';
console.log(`CHK${index} = ` + window[`CHK${index}`]);
}
}
We are lacking some information about your environment, but assuming your framework gives you a set of global variables, this should get you started:
for (var i=1, i<=99, i++) {
var i_padded = i.toString().padStart(3, 0);
if (window["VAL" + i_padded] == 'Checked') {
window["CHK" + i_padded] = '✓';
} else {
window["CHK" + i_padded] = "";
}
}
In order to access your global variables I am using the window object here. This is assuming you are running this JS in a browser or browser-like environment.

Javascript how return works

I actually want to update my previous question Javascript understanding return because the code below is quite similar to the previous one but since that question was answered already I decided to post this. The code of my previous questions works fine already but I want to satisfy some of my curiosities so I experimented the code and moved the return namePosition,
function positionIdentifier(name, nameArray) {
var namePosition;
for (i = 0; i < nameArray.length; i++) {
if (nameArray[i] == name) {
namePosition = function() {
alert("Your name is in position number " + (i + 1));
}
}
}
return namePosition;
}
name1Array = ["look", "sky", "walk", "kier"];
positionIdentifier("walk", name1Array)();
Why does it alert the wrong position (i+1)? Instead it alerts the final position which is the length of the array.
You forgot to use break statement here is correct code:
<script>
function positionIdentifier(name, nameArray) {
var namePosition;
for (i = 0; i < nameArray.length; i++) {
if (nameArray[i] == name) {
namePosition = function () {
alert("Your name is in position number " + (i + 1));
};
break;
}
}
return namePosition;
}
name1Array = ["look", "sky", "walk", "kier"];
positionIdentifier("walk", name1Array)();
</script>
That my friend is what is called a closure in javascript.
function() {
alert("Your name is in position number " + (i + 1));
}
When positionIdentifier function is invoked, i has the last value from the for loop.
To fix this you need to do this
function positionIdentifier(name, nameArray) {
var namePosition;
for (i = 0; i < nameArray.length; i++) {
if (nameArray[i] == name) {
/* now this will keep the correct value of i */
namePosition = (function(i) {
return function(){
alert("Your name is in position number " + (i + 1));
}
})(i)
/* now this will keep the correct value of i */
}
}
return namePosition;
}
Here is a working fiddle https://jsfiddle.net/uxyot51b/

Why does my ng-class display even when false

I have an image I'm trying to shake when a user guesses the name of a fish wrong. I'm using a conditional ng-class="{'shake':error}". However, even when the answer is correct the image shakes. I don't believe that at anytime $scope.error is set to true. What am I missing here?
codepen
I think what you want to do is return guessIsCorrect or guessIsWrong from your compare function.
$scope.compare = function(guess) {
guess = guess.replace(/\s/g, '').toLowerCase();
var answers = [];
answers.push($scope.name.replace(/\s/g, '').toLowerCase());
currentAnimal.alts.forEach(function(alt) {
answers.push(alt.toLowerCase().replace(/\s/g, ''));
});
//console.log(answers);
//console.log("Guess: " + guess + "\n");
//console.log("Answer: " + answers + "\n");
for (var x = 0; x <= answers.length; x++) {
if (guess === answers[x]) {
return guessIsCorrect();
}
if (x === answers.length) {
return guessIsWrong();
}
}
};

javascript function return "undefine" from jsp page

i have a jsp page and call a JS function which is in some abc.js file from this JSP page.
i have included this js file to jsp page.
JSP JavaScript Code:-
function doFinish(tableId, col, field)
{
var oldselectedCells = "";
var selItemHandle = "";
var selRightItemHandle = "";
var left = -1;
var right = -1;
// Get the table (tBody) section
var tBody = document.getElementById(tableId);
// get field in which selected columns are stored
var selectedCellsFld = document.getElementById(tableId + datatableSelectedCells);
selectedCellsFld.value = oldselectedCells;
for (var r = 0; r < tBody.rows.length; r++)
{
var row = tBody.rows[r];
if (row.cells[col].childNodes[0].checked == true)
{
selectedCellsFld.value = oldselectedCells +
row.cells[col].childNodes[0].id;
selItemHandle = row.cells[col].childNodes[0].value
oldselectedCells = selectedCellsFld.value + datatableOnLoadDivider;
left = selItemHandle.indexOf("=");
right = selItemHandle.length;
selRightItemHandle = selItemHandle.substring(left+1,right);
var index=getColumnIndex(tBody,"Name");
if(index!=null)
{
if(field == 1)
{
window.opener.document.TemplateForm.eds_asbactionscfg_item_handle_child_physpart.value = selRightItemHandle;
window.opener.document.TemplateForm.ChildPhysicalPart.value = row.cells[index].childNodes[0].innerHTML;
}
else if (field == 2)
{
window.opener.document.TemplateForm.eds_asbactionscfg_dev_doc_item_handle_name.value = selRightItemHandle;
window.opener.document.TemplateForm.DeviationObject.value = row.cells[index].childNodes[0].innerHTML;
}
else if (field == 3)
{
window.opener.document.TemplateForm.eds_asbactionscfg_dev_doc_item_handle_name.value = selRightItemHandle;
window.opener.document.TemplateForm.DeviationObject.value = row.cells[index].childNodes[0].innerHTML;
}
}
}
}
window.close();
}
JS Code:-
function getColumnIndex(tBody,columnName)
{
var cells = tBody.parentNode.getElementsByTagName('th');
for (var i=0;i<cells.length; i++)
{
if(cells[i].hasChildNodes())
{
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
}
}
}
i had debug this code with firebug & calling getColumnIndex(tBody,columnName) function works fine but when it return to caller the var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
suggest some solution.
getColumnIndex(tBody,columnName) function works fine.
as if it matches this if condition
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
so that it returns something.
but when you replace this
var index=getColumnIndex(tBody,"Name"); so that coulmnName would be "Name" in String.
And it doesn't match with any columnName so that your condition going to be wrong and function doesn't return anything.
var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
suggestion is put some else condition on that and return some error message like this :
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
} else{
// put some error message
// return null
}
i had debug this code with firebug & calling getColumnIndex(tBody,columnName) function works fine
From this, I'm assuming that there isn't anything wrong with the implementation of your getColumnIndex function, so your issue with getting an undefined value must have to do with when this function is returning a value.
but when it return to caller the var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
This leads me to assume that your tBody variable is not being set correctly, given that the "function works fine".
I'm assuming there is a case in your code where the conditions of your getColumnIndex function is not met.
function getColumnIndex(tBody,columnName)
{
var cells = tBody.parentNode.getElementsByTagName('th');
for (var i=0;i<cells.length; i++)
{
if(cells[i].hasChildNodes())
{
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
}
}
// If your code reaches this point, then the prior conditions have not been met
// You can choose to do something else here for return false/undefined etc.
return undefined;
}

Categories

Resources