Unusal JS code section - javascript

So I am a total JS novice, but I had someone write some code to be used within Qualtics survey. That code is now broken and I am trying to fix it up, there is an error with this line:
var timingObj=${e://Field/TimingObj};
I was hoping someone could help explain this line to me so I may be able to fix it up. (I have to full code, but that seemed a bit long to have someone go through.) I understand the first part is setting the variable named timingobj equal to something.
What that is is confusing to me, especially with the "//" which I understand to denote a note and not code. This code had worked in the past but now gives me an error. Any help understanding what I am working with would be amazing.
Thanks!
Update full code as provided to me:
//configurations
var bindInterval=10;
//initiate
var timingObj=${e://Field/TimingObj};
timingObj.version=3;
var startTiming=function(tag){
var currentTimeObj=timingObj[tag];
if (!currentTimeObj) {
currentTimeObj={};
currentTimeObj.startTimes=[];
currentTimeObj.elapseTimes=[];
currentTimeObj.totalElapsed=0;
timingObj[tag]=currentTimeObj;
}
var time=(new Date()).getTime();
currentTimeObj.startTimes.push(time);
currentTimeObj.startTime=time;
timingObj.activateTag=tag;
};
var closePopupCallback=function(){
//stop timing
var time=(new Date()).getTime();
var currentTag = timingObj.activateTag;
var currentTimeObj = timingObj[currentTag];
var elapsed=time - currentTimeObj.startTime;
currentTimeObj.elapseTimes.push(elapsed);
var totalElapsed=currentTimeObj.totalElapsed + elapsed;
currentTimeObj.totalElapsed=totalElapsed;
Qualtrics.SurveyEngine.setEmbeddedData(currentTag+'_Time',totalElapsed);
Qualtrics.SurveyEngine.setEmbeddedData(currentTag+'_Count',currentTimeObj.startTimes.length);
var timingObjSerialized=Object.toJSON(timingObj);
Qualtrics.SurveyEngine.setEmbeddedData('TimingObj',timingObjSerialized);
};
var bindCloseEvent=function() {
//window.document.observe('dom:loaded',func) and document.observe('dom:loaded',func) did not work
var closeButton=$('bottomNavClose');
if (closeButton) {
closeButton.observe('click', closePopupCallback);
} else{
setTimeout(bindCloseEvent,bindInterval);
}
};
// bad smell
var bindLightBoxCloseEvent=function(){
var lightBox=$('lightbox');
if (lightBox) {
lightBox.observe('click', closePopupCallback);
} else{
setTimeout(bindLightBoxCloseEvent,bindInterval);
}
};
bindLightBoxCloseEvent();
bindCloseEvent();
Qualtrics.SurveyEngine.addOnload(function(){
});

This should be an easy fix:
try:
var timingObj = "${e://Field/TimingObj}";
When using Qualtrics piped text in JavaScript, you must enclose the piped text in quotes.
If the piped text is a number that you plan on using and manipulating, as I am assuming, you should use parseInt() to ensure you don't have issues:
var timingObj = parseInt("${e://Field/TimingObj}");
Since the full code makes it clear that the item you are passing in is meant to be an object, I am assuming it is being passed in valid JSON, you should parse it as such:
var timingObj = JSON.parse("${e://Field/TimingObj}");

Related

How to click on Button until a dependent value reaches a certain value?

With Protractor I'm trying to automate a part of a UI, in which with plus-minus buttons a min-max-range (i.e. 100'000-200'000) is selected.
There is an inputAmount (i.e. 250'000), defining the range to select.
Now I should click to the plus-button until I get a range 200'000-300'000, identified by the dynamically adjusted <div class="currentMax">100'000</div>.
My issue is to convert the currentMax of the currently selected range from String to a number, so I can compare it to the inputAmount.
The HTML looks like this:
<html>
<div class="max">100'000</div>
<div class="slider-buttons">
<div class="slider-button">
<div class="slider-button-plus"></div>
</div>
<div class="slider-button">
<div class="slider-button-minus"></div>
</div>
</div>
</html>
My protractor code looks something like this:
this.selectAmountRange = function() {
var currentMax = $('.max').getText(); //How do I get this value as number?
var btnPlus = $('.slider-buttons .slider-button-plus');
for (i = currentMax; i <= inputAmount; i = currentMax) {
btnPlus.click();
browser.wait(300);
};
};
One problem is that currentMax is thousand-delimited with "'", so I can't convert it straight forward.
The other problem is, that getText() returns a promise, but even though I found some examples I failed so far to get the string and even remove the delimiter.
I tried this, but didn't work.
var currentMax = $('.max').getText().then(function(value){
value.replace(/\'/,'');
});
As I'm quite new to JavaScript and Protractor, I assume it's just some syntax I'm doing wrong.
Grateful for any advice and a nudge towards a doable function.
Since javascript is asynchronous, you need to resolve the promise before accessing the return value from the promise. In your case, you need to implement recursion to make your method to click the slider until required value is set. Have a look at below example.
var selectAmountRange = function(inputAmount) {
$('.max').getText().then(function(currentMax){
var btnPlus = $('.slider-buttons .slider-button-plus');
if(currentMax < selectAmountRange){
btnPlus.click();
browser.sleep(3000);
selectAmountRange(inputAmount); //call the same method untill required inputAmount is selcted.
}
};
};
I don't have the right to comment so I put it as an answer, did you put all your code inside then() - after the promise returns the value ?
The currentMax is a variable initialized once, does the loop ends once it enters it !?
You mentioned currentMax and in code there is max are they the same ? what does the btnPlus click increments, the max ?
Finally I would suggest something similar to:
$('.max').getText().then(function(value){
var currentMax = parseInt(value.replace(/\'/,''));
var btnPlus = $('.slider-buttons .slider-button-plus');
for (i = currentMax; i <= inputAmount; i++) {
btnPlus.click();
browser.wait(300);
};
});

javaScript parseFloat is not working

parseFloat is not working in our function
There are so many answers abouth this issue but any of them are not satisfying for me.
Please see code:
var kasatoplam = $('.kasatoplam');
function fiyatTopla() {
var eklenen = $('.adisyon .adisyontable .adsiyonTableUrunler').length;
var urunfiyatlari = [];
$('.adisyon .adisyontable .adsiyonTableUrunler').each(function () {
var urunfiyat = $(this).find('.urunFiyati').text()
urunfiyatlari.push(parseFloat(urunfiyat))
});
console.log(urunfiyatlari);
var toplamfiyat = 0;
for(i=0; i<=urunfiyatlari.length; i++){
toplamfiyat += urunfiyatlari[i]<<0;
}
kasatoplam.html(toplamfiyat);
}
`var urunfiyat = $(this).find('.urunFiyati').text()
urunfiyatlari.push(parseFloat(urunfiyat))`
What is the failure mode for parseFloat? Is it throwing an exception or just not yielding the values you expected? It would help to know.
In any case, it seems that the contents of urunfiyat must be the problem. Have you stepped through the code with the debugger and seen what kind of text values you are getting from
` $(this).find('.urunFiyati').text()' ?
Perhaps you are getting nothing undefined or empty string or perhaps the string needs a little processing before it is passed to parseFloat

Simplifying a javascript function with repeated similar lines (with a loop?)

Okay, I hope you don't all facepalm when you see this - I'm still finding my way around javascript.
I am putting together an RSVP form for a wedding website.
I want the guests to be able to add their names to the RSVP form, but only have as many fields showing as required. To this end, after each name field, there is a link to click, which will, when clicked, show a name field for the next guest.
The code below works... but I am sure it can be tidier.
I have tried to insert a for() loop into the code in several different ways, I can see that the for() loop increments correctly to the last value - but when it does so, it leaves only the last addEventListener in place. I can only assume, that I should be using a different kind of loop - or a different approach entirely.
How should I tidy up the following?
<script>
function showNextGuest(i) {
document.getElementsByTagName(\'fieldset\')[i].style.display = \'block\';
}
function initiateShowNextGuest() {
document.getElementsByTagName('fieldset')[0].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(1);},false);
document.getElementsByTagName('fieldset')[1].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(2);},false);
document.getElementsByTagName('fieldset')[2].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(3);},false);
document.getElementsByTagName('fieldset')[3].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(4);},false);
document.getElementsByTagName('fieldset')[4].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(5);},false);
}
window.onload = initiateShowNextGuest();
</script>
Your intuition is right - a for loop could indeed simplify it and so could a query selector:
var fieldsSet = document.querySelectorAll("fieldset"); // get all the field sets
var fieldss = [].slice.call(asSet); // convert the html selection to a JS array.
fields.map(function(field){
return field.querySelector("a"); // get the first link for the field
}).forEach(function(link, i){
// bind the event with the right index.
link.addEventListener("click", showNextGuest.bind(null, i+1), false);
});
This can be shortened to:
var links = document.querySelectorAll("fieldset a:first-of-type");
[].forEach.call(links, function(link, i){
link.addEventListener("click", showNextGuest.bind(null, i+1), false);
});
function nextGuest () {
for(var i = 0; i < 5; i++){
document.getElementsByTagName('fieldset')[i]
.getElementsByTagName('a')[0]
.addEventListener('click',function(){
showNextGuest(parseInt(i + 1));
}, false);
}
}
Benjamin's answer above is the best given, so I have accepted it.
Nevertheless, for the sake of completeness, I wanted to show the (simpler, if less elegant) solution I used in the end, so that future readers can compare and contrast between the code in the question and the code below:
<script>
var initiateShowNextGuest = [];
function showNextGuest(j) {
document.getElementsByTagName('fieldset')[j].style.display = 'block';
}
function initiateShowNextGuestFunction(i) {
return function() {
var j = i + 1;
document.getElementsByTagName('fieldset')[i].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(j);},false);
};
}
function initiateShowNextGuests() {
for (var i = 0; i < 5; i++) {
initiateShowNextGuest[i] = initiateShowNextGuestFunction(i);
initiateShowNextGuest[i]();
}
}
window.onload = initiateShowNextGuests();
</script>
In summary, the function initiateShowNextGuests() loops through (and then executes) initiateShowNextGuestFunction(i) 5 times, setting up the 5 anonymous functions which are manually written out in the code in the original question, while avoiding the closure-loop problem.

Cannot read property "0" from undefined. Error Google Apps Script

I´m geting Cannot read property "0" from undefined. Error on line 16. ( while (colunaDias[emptyCell][0] <= dias) )
It should be a very simple function for google SpreadSheets. I can´t see what I´m doing wrong...
The bizarre thing is that if I don´t use the variable "dias" and use a integer instead. The while function works....
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var leadsSheet = ss.getSheetByName("Leads Todas as Categorias, menos outros - Days (5days)");
var targetSheet = ss.getSheetByName("feticaria");
var cellLeads = leadsSheet.getRange(1,1).getValue();
//var cellTarget = targetSheet.getRange(1,1).setValue(valor);
var colunaDias = leadsSheet.getRange('B:B').getValues();
var sourceMedium = leadsSheet.getRange('A:A').getValues();
var emptyCell = 16;
var dias = 1;
while (colunaDias[emptyCell][0] != ""){
while (colunaDias[emptyCell][0] <= dias){
dias++;
emptyCell++;
}
emptyCell++;
}
Logger.log(emptyCell);
}
I think the only thing that could cause that error is if emptyCell is bigger than the colunaDias array, i.e. if your sheet is smaller than 16 rows (if the value you show here is correct).
Add this line right before the first while :
Logger.log('emptyCell = '+emptyCell+' and colunaDias.length = '+colunaDias.length);
I tested a copy of your script and it runs without error except if I define emptyCell > 1000 on a 1000 rows Sheet.
I'm guessing that colunaDias[emptyCell] is undefined. It passes the first while condition, because "undefined" is not equal to "". If colunaDias[emptyCell] is undefined, then either there is something wrong with this line:
var colunaDias = leadsSheet.getRange('B:B').getValues();
or
colunaDias[emptyCell][0]
Where "emptyCell" is 16 is the problem. getValues() returns an object of rectangular grid of values. I would test to see if there is anything in the rectangular grid, by checking [0][0].
Logger.log('is there any data? ' + colunaDias[0][0])
If there is no data, then the something failed on line:
var colunaDias = leadsSheet.getRange('B:B').getValues();
If that line is working, then something higher up is wrong.
You should be checking the return type of getSheetByName for null.
// The code below will log the index of a sheet named "YourSheetName"
var leadsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("YourSheetName");
if (leadsSheet != null) {
Logger.log(leadsSheet.getIndex());
}
I know this post in from a few years ago, but this was the closest to a problem I am having. I just want to post my answer in case someone else ends up here one day.
For some reason the check variable "dias" is being passed as a string.
That's why replacing it with a number allows the script to run.
dias = parseInt(dias); //use this before the variable needs to be read
I cant say why it is passing a string after being ++ but this will fix it

detect the end of asynchronous recursion

var checkduplicates = new Array();
drawOne(i);
//console.log(checkduplicates)
function drawOne(i)
{
//randomly select one photo
var picinfo = photos[Math.floor(Math.random()*photos.length)];
//check duplicates pic, if duplicates exist, get another one
while(checkduplicates.indexOf(picinfo)!=-1||picinfo.title.length>10)
{
picinfo = photos[Math.floor(Math.random()*photos.length)];
}
checkduplicates.push(picinfo);
var ctx = document.getElementsByClassName("canvas")[i].getContext('2d');
var img = new Image();
//get the pic URL
img.src = "http://farm" + picinfo.farm + ".static.flickr.com/"
+ picinfo.server + "/" + picinfo.id + "_" + picinfo.secret + "_m.jpg";
img.onload = function()
{
// Draw pieces
ctx.drawImage(img,0,0,132,150);
ctx.drawImage(frame,0,0,133,152);
if(picinfo.title=="")
$("#"+i).append("Untitled");
else
$("#"+i).append(picinfo.title);
i++;
if (i != canvaslength)
{
drawOne(i);
}
}
What I am doing here is that I am dynamically generate pictures to fill out 16 canvas and some people said that I am using asynchronous recursion which I dont even notice. I have tried to use loop instead of recursion but somehow ended it up getting exception that i dont know how to fix. So I stick to recursion. However, my problem is that how I can detect the end of the recursion like the commented line shows there is only one item in the array.
//console.log(checkduplicates)
and the explanation I got is that as I understand, the commented console.log is executed before a bunch of recursion of drawOne function finished But what I wanted was that I wanted the full 16 images to be fully loaded and then select them so that I can do something with them. Therefore, the question is how I can detect the end of the recursion. Thank you. You are welcomed to ignore most of my codes and just look at the recursion part.
This is not 'asynchronous recursion'. That would imply that at least two of these loops are running at the same time, and they return asynchronously. Which is simply not the case.
Basically the only time you STOP recursion is when i == canvaslength.
So, just take that if statement.
if (i != canvaslength)
{
drawOne(i);
}else{
console.log('recursion is done') // do what you want here.
}

Categories

Resources