Reading embedded data into Qualtrics in a generalized way - javascript

I have a set of embedded variables, q1_ans, q2_ans, q3_ans, ....
In one of my questions, I would like to read in all these vars one at a time as part of a loop:
Qualtrics.SurveyEngine.addOnload(function()
{
var i;
for (i = 1; i <= 2; i++) {
let ans_q = "e://Field/q" + i + "_ans"
let ans = "${" + ans_q+ "}";
console.log(ans);
}
});
However, this is not reading in the embedded data values. How can I read in my embedded variables in a JS loop?

Embedded data fields are resolved on the server before the page is sent to the browser, so you can't dynamically build and resolve the pipe strings for embedded data fields in JavaScript.
You could pipe all the strings into an array definition, then loop through the array.
Another alternative would be to use Qualtrics.SurveyEngine.getEmbeddedData() if it still works (no longer included in documentation).

Related

Searching a string

OK Ive been able to get the following to partially work
var Global_Wound_array =[{"WoundNumber":1,"BodySide":"Front","BodyPart":"Nose"},{"WoundNumber":2,"BodySide":"Left","BodyPart":"Head"},{"WoundNumber":3,"BodySide":"Back","BodyPart":"Ear"}]
var Global_Wound_Counter = 1
I can get the page to loop through and display the individual wounds but I need a way to say at a particular page one of the values eg on WoundNumber 2 BodyPart has changed and updated the string without affecting the rest of it.
page9200.setEventHandler("pageFinishing", function () {
//getSelectedButtonLabel this is ok - specific on the system
let Q1 = Q3_WoundNumber.getValue();
let Q2 = Q1_BodySide.getSelectedButtonLabel();
let Q3 = Q2_BodyPart.getSelectedButtonLabel();
for (var i = 0; i < Global_Wound_array.length; i++) {
if (i+1 == Q1){
//create new temp variable array
var Temp_Wound_obj2 = {"WoundNumber": Q1,"BodySide": Q2,"BodyPart":Q3}
Global_Wound_array.push(Temp_Wound_obj2)
}
}
});
As well as being able to reach the end of the string to present a blank set of values to have the option to add a new wound.
Every time I think Ive got something that looks like it would work I go around in circles, when I try to update the system at the end I get and error that the - invaid parameters for RPC call: variable is bad
It seems you are pasting JSON onto JSON, with no separator. This creates a messy and non-standard data structure. If you wrote your JSON with a newline at the end, you would end up with a JSONL file, which is very simple to process.
const jsonl = `
[{"WCount":1,"Side":"Centre","Part":"Ocipit","Type":"Other","SurroundingSkin":"Dermatitis","Height":"","Width":"","Depth":""}]
[{"WCount":2,"Side":"Front","Part":"Neck","Type":"Diabetic foot wound","SurroundingSkin":"Healthy/intact","Height":"3","Width":"4","Depth":"5"}]
`;
const jsonItems = jsonl.trim().split("\n");
const lastJsonItem = jsonItems[jsonItems.length - 1];
const lastItem = JSON.parse(lastJsonItem);
const lastWCount = lastItem[0].WCount;
console.log(lastWCount);
If you already have a file without newlines... it would be best to insert them, and correct your data to JSONL. This is simple in your case just by replacing ][ with ]\n[ (and making sure the file ends with a newline too, so the next write would not be messed up), since you have no nesting and (hopefully) no ][ in your text, but in general it is not easy - I don't know of a JSON parser that will return unconsumed text, so it would probably involve writing a JSON parser. Much easier to write data correctly in the first place.

Access session attributes in javascript

I need some help understanding how to do what I suppose it should be an easy thing.
In my controller I am converting a PDF into several images, and I can easily get page number being dealt with and total number of pages that needs to be doing. I am putting both in the session with:
request.getSession().setAttribute("currentPageNumber", currentPageNumber);
request.getSession().setAttribute("totalPagesNumber", totalPagesNumber);
On the view I'd like to show a progress bar knowing these values, doing something like CeilingOf((currentPageNumber/totalPagesNumber)*100) but I don't know how to continuously get those updated values.
If I use:
'<%= request.getSession().getAttribute("currentPageNumber")%>'
this will be resolved loading the page (before the session is even being updated with the attributes) and both show up null.
What do I need to do to access these values on the view? Thank you very much for your help
You can't get that directly. Because JavaScript is executed on the client side (browser), and the session data on the server.
Some ways to use session attribute variables in JavaScript:
a hidden input field storing the variable as its value and reading it through the DOM API
an HTML5 data attribute which you can read through the DOM
storing it as a cookie and accessing it through JavaScript
injecting it directly in the JS code, if you have it inline
In JSP:
<input type="hidden" name="totalPagesNumber" value="${sessionScope.totalPagesNumber} />
Javascript:
var inputs = document.getElementsByTagName("input"), len = inputs.length, i, totalPagesNumber;
for (i = 0; i < len; i++) {
if (inputs[i].name == "totalPagesNumber") {
totalPagesNumber = inputs[i].value;
break;
}
}
Here i am looping all the input hidden fields so that you can get all the values in one loop.
${currentPageNumber}
will access the attribute

Java Script + JSON Parsing and converting into Array

I'm looking for help in converting a particular elements in JSON message to an array using java script at run time. We wanted the script to be more generic. Actually we were trying the following which worked for single element and while changing it to handle for multiple elements at run time its not working.
//Working for Single element - Static
var bodyContext = JSON.parse(response.content)
if(bodyContext.companylist.company.constructor !== Array){
bodyContext.companylist.company = [bodyContext.companylist.company]
}
The above code works and converts Company in JSON message as a Array, Where as the below we tried for multiple elements is not working
//Not Working for multiple elements - dynamic
var bodyContext = JSON.parse(response.content)
var elementName = "";
//Loop runs every time and changes the value of elementName at every iteration
if(bodyContext.elementName .constructor !== Array){ //not working
bodyContext.elementName = [bodyContext.elementName] //Not working
}
instead of looking for "bodyContext.companylist.company" and converting into Array, "bodyContext.elementName" is checked and added to the bodycontext object.
how to handle this. ElementName variable along with JavaScript object is not recognized.
Please help.
you can JSON.parse(data) then you can fetch data from Javascript object like
$.each(Obj,function(key,value){
});
You'll want to use
bodyContext[elementName]
since
bodyContext.elementName
looks for a field in bodyContext named elementName, not the a field named after the value in elementName.
Also, you initialize elementName with "", and this won't match anything on the first iteration.

Is there a way cleanly use hidden classes in javascript when you dont know what the properties will be?

I have a (GIS) project which displays large amounts of customer data (Thousands of records) to clients. Where nescessary/possible/required, we use server side pagination/filtering/data manipulation but there are cases where it is most efficient to send the data in JSON format to the client and let their browser do the filtering.
The amount of data is large, so we format it to save on bandwidth and parsing time - instead of individual objects, we send a structure that includes the attribute names first and then the values in a single flat array. On the client, we rebuild this into more traditional json objects before other processing occurs. eg:
{attrNames:["foo","bar"],values:[1,2,3,4,...]) -> [{foo:1,bar:2},{foo:3,bar:4},...]
The code for doing this looks a little like this:
function toObjectArray(attrNames, values){
var ret = [];
var index = 0;
var numAttrNames = attrNames.length;
var numValues = values.length;
while(index < numValues){
var obj = {};
for(var a = 0; a < numAttrNames; a++){
obj[attrNames[a]] = values[index++];
}
ret.push(obj);
}
return ret;
}
Given that the attributes may change depending on the customer data, is there a way to do this translation that takes advantage of hidden classes in modern javascript engines like V8? I have done some micro benchmarks similar to our use case ( http://jsfiddle.net/N6CrK/1/ ) where working with json such that hidden classes are used is orders of magnitude faster than building the objects as above. I can get some of this boost using "eval" to create objects, but this feels ugly (This is demonstrated in the js fiddle). Is there a better way? Perhaps using some variant of Object.create, or something like it?
You mean something like this right?
function toHiddenObjectArray(attrNames, attrValues){
var numAttrNames = attrNames.length,
numValues = attrValues.length;
function Data( values ) {
for(var v = 0; v < numAttrNames; v++) {
this[attrNames[v]] = values[v];
}
}
var ret=[];
for( var i=0; i<numValues ; i+=numAttrNames ) {
ret.push( new Data( attrValues.slice(i,i+numAttrNames) ) );
}
return ret;
}
You can check our the fiddle here: http://jsfiddle.net/B2Bfs/ (With some comparison code). It should use the same "Hidden Class" (i.e. Data). Not sure how much quicker it is though!
But, if you really want to make your code none blocking, why not load the page, then request the data via AJAX, then run all you code when you get a response.
I can get some of this boost using "eval" to create objects, but this feels ugly
There's a less ugly way using the Function constructor. Also, further optimisations can be done by immediately assigning the values to the properties, instead of initialising them with null and then again iterating through the attrs array like the adHoc does it. You'd just pass each of the rows you get in the response (array? string? byte-whatever?) as a parameter to the factory.
Also I've moved the creation of the factory function out of the create function, so that only one function will be instantiated (and optimized after enough calls to it).
A decent amount of the time in your test loop is spent on the getTotal, so I've optimised this in a similar manner. Not using getTotalAdHoc in testing the optimised solution drastically reduces the measured time (you can test with getTotalOptimum as well).
var factory = new Function("arr", "return{"+attrs.map(function(n, i){
return n+":arr["+i+"]";
}).join(",")+"};");
var getSum = new Function("o","return "+attrs.map(function(n, i){
return "o."+n;
}).join("+")+";");
(updated jsfiddle)
I haven't yet tried moving the complete loop into the generated code, which could avoid a few function calls, but I don't think this is necessary.
For some reason I just recalled this question... and I just came up with a solution that is way dirtier than using eval but which causes a huge speed boost. The downside of it is that code will be similarly little maintainable as when using eval.
The basic idea is: When receiving the attribute names, generate the function code to parse the following data in JavaScript and add it in a <script> tag to the <head>.
Yeah, isn't that dirty? :-)
If performance is so critical for you, it will definitely help you... here's a modified version of your microbenchmak that proves it: http://jsfiddle.net/N6CrK/17/
Some remarks on the code...
The two functions createWithGeneratedFunction and getTotalWithGeneratedFunction are simply wrapper functions that can be used by productive code. All they do is make sure that the <script> with the generated functions is set up and then call it.
function createWithGeneratedFunction(numValues){
makeSureScriptsAreSetUp()
return createWithGeneratedFunctionAdded(numValues);
}
function getTotalWithGeneratedFunction(objs){
makeSureScriptsAreSetUp()
return getTotalWithGeneratedFunctionAdded(objs);
}
The actual workhorse is the makeSureScriptsAreSetUp with the functions it creates. I'll go through it line by line:
function makeSureScriptsAreSetUp() {
if(scriptIsSetUp)
return;
If the required <script> tag was already set up this function will directly return since there is nothing to do for it anymore.
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
var theFunctions = "";
This prepares the creation of the required functions. The theFunctions variable will be filled with the code that is going to be put into the <script> tag content.
theFunctions =
"function createWithGeneratedFunctionAdded(numValues) {" +
" var ret = [];" +
" var value = 0;" +
" for(var i = numValues; i-- > 0;) {" +
" ret.push({";
for(var attr in attrs) {
theFunctions +=
" " + attrs[attr] + ": value++,";
}
theFunctions +=
" });" +
" }" +
" return ret;" +
"}" +
"";
This completes the code for the parsing function. Obviously it just "parses" the numbers 0 to numValues in this microbenchmark. But replacing value++ with something like TheObjectThatTheClientSentMe.values[value++] should bring you very close to what you outlined in your question. (Obviously it would make quite a lot of sense to rename value to index then.)
theFunctions +=
"function getTotalWithGeneratedFunctionAdded(objs) {" +
" var ret = 0;" +
" for(var i = objs.length; i-- > 0;) {" +
" var obj = objs[i];" +
" ret += 0";
for(var attr in attrs) {
theFunctions +=
" + obj." + attrs[attr];
}
theFunctions +=
" ;" +
" }" +
" return ret;" +
"}";
This completes the code for the processing function. Since you seem to require several processing functions, especially this code could become somewhat ugly to write and maintain.
script.text = theFunctions;
head.appendChild(script);
scriptIsSetUp = true;
}
In the very end we simply set the <script> tag content to the code we just created. By then adding that tag to the <head>, Chrome's hidden class magic will occur and will make the code VERY fast.
Concerning extensibility: If you have to query different attribute/value sets from the server on the same page, you might want to give each parsing/processing method set unique names. For example, if you first receive attrs = ["foo","bar"] and next attrs = ["foo","bar","baz"] you could concat the underscore-joined attribute name array to the generated function names.
For example, instead of using createWithGeneratedFunctionAdded you could use createWithGeneratedFunctionAdded_foo_bar for the first attribute/value set and createWithGeneratedFunctionAdded_foo_bar_baz for the second attribute/value set. An attr parameter could then be added to the wrapper functions that will be used to generate the correct code line for an eval (yes, here the evil eval would return) to trigger the correct generated function. Obviously, the attr parameter would also be required for the makeSureScriptsAreSetUp function.

Detect if string contains javascript tags using jQuery/JavaScript

I am trying to create a very simplistic XSS detection system for a system I am currently developing. The system as it stands, allows users to submit posts with javascript embedded within the message. Here is what I currently have:-
var checkFor = "<script>";
alert(checkFor.indexOf("<script>") !== -1);
This doesn't really work that well at all. I need to write code that incorporates an array which contains the terms I am searching for [e.g - "<script>","</script>","alert("]
Any suggestions as to how this could be achieved using JavaScript/jQuery.
Thanks for checking this out. Many thanks :)
Replacing characters is a very fragile way to avoid XSS. (There are dozens of ways to get < in without typing the character -- like < Instead, HTML-encode your data. I use these functions:
var encode = function (data) {
var result = data;
if (data) {
result = $("<div />").html(data).text();
}
};
var decode = function (data) {
var result = data;
if (data) {
result = $("<div />").text(data).html();
}
};
As Explosion Pills said, if you're looking for cross–site exploits, you're probably best to either find one that's already been written or someone who can write one for you.
Anyway, to answer the question, regular expressions are not appropriate for parsing markup. If you have an HTML parser (client side is easy, server a little more difficult) you could insert the text as the innerHTML of an new element, then see if there are any child elements:
function mightBeMarkup(s) {
var d = document.createElement('div');
d.innerHTML = s;
return !!(d.getElementsByTagName('*').length);
}
Of course there still might be markup in the text, just that it's invalid so doesn't create elements. But combined with some other text, it might be valid markup.
The most effective way to prevent xss attacks is by replacing all <, > and & characters with
<, >, and &.
There is a javascript library from OWASP. I haven't worked with it yet so can't tell you anything about the quality. Here is the link: https://www.owasp.org/index.php/ESAPI_JavaScript_Readme

Categories

Resources