Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

SheetWorker Script - variable scope

1496152068

Edited 1496152089
Loki
Sheet Author
Hi! I have the following structure to get all the names of all rows of a repeating section (with abilities that buff the character): on("change:repeating_superpowers", function(eventinfo) {     var superpowers = new Array();     superpowers.push("test1");     getSectionIDs("repeating_superpowers", function(idarray) {         _.each(idarray, function(currentID, i) {             getAttrs(["repeating_superpowers_" + currentID +"_spvalue", "repeating_superpowers_" + currentID +"_spname"], function(v) {                 var superpowername = v["repeating_superpowers_" + currentID + "_spname"];                 var superpowervalue = v["repeating_superpowers_" + currentID + "_spvalue"];                 superpowers.push("test2");                 if (superpowername.toLowerCase() == "lasereyes") {                     setAttrs({                         lasereyes: 1                     });                 } else if (superpowername.toLowerCase() == "wings") {                     setAttrs({                         speed: 10                     });                 }               });         });     });     setAttrs({         testfield: superpowers.length     }); }); Let's assume that the attribute names and the fields are correct (because it works basically). What I want is to get an array that holds the names of all (e.g.) super powers. I think it should work the way it is right now, but if I try the code, it writes "1" in "testfield", but it should write 2. So I guess that there is a problem with the scope of the array superpowers. But since the variable is declared right in the beginning, I don't have a clue why it shouldn't work. Can anyone give me a helpful tip regarding this problem? Greetings, Loki
1496166772
Lithl
Pro
Sheet Author
API Scripter
The problem is not about scope, it about asynchronous operations. getSectionIDs and getAttrs are examples of asynchronous functions -- you tell the system to do thing X, and it will get around to doing them eventually, but in the mean-time the system will do what you've written after asking it to do thing X. In this case, your code is essentially asking Roll20 to: Create an array named "superpowers" Add the string "test1" to the superpowers array Eventually, give me all the "repeating_superpowers" ids, but it doesn't have to be now Set the "testfield" attribute to the length of the superpowers array When the system eventually gets around to doing step #3, it will call your function(idarray) function to execute the rest of your code. But long before that happens, it has already done step #4. The simplest solution to this problem is to move your setAttrs call into the deepest-nested asynchronous callback in your code (in this case, inside the getAttrs call). Other, more complicated solutions exist as well, but this code is simple enough that those solutions probably aren't needed.
1496169474

Edited 1496169631
Loki
Sheet Author
Hi, Brian. Thanks for your response! This is only a small part of the code - in reality the if-else statement gets much longer (but that doesn't matter for this problem). What I want to do with the list of superpowers is to check if there is a specific one (e.g. lasereyes) and if not, make certain changes. Because of this I need the information about the superpowers that are in the array outside the asynchronous (thanks for this explanation, I really didn't get this before) functions. My current try of a workaround is this: ... superpowers.push(superpowername); setAttrs({   dummy: superpowers ); ... And later on (outside of getSectionIDs): getAttrs(["dummy"], function(v) { if (v.dummy.indexOf("lasereyes") == -1)         {         setAttrs({                     hiddenmalus: 0                 }); } }); That doesn't work very well, because (I guess) the indexOf sometimes returns -1 and sometimes not, depending on how fast the asynchronous functions work. This whole part of the code is only for the case that an element of the repeating section gets removed - in this case the changes (hidden notations) should also be revoked. So the code iterates through the repeating sections and checks if the hiddenmalus is 1 AND there is no entry of "lasereyes" in the repeating section and then sets it to 0 (because within the remove event of the repeating section I couldn't manage it - I opened a thread for it here ). So if you could give me a hint regarding the core-problem maybe I could abandon this code anyway. I'm also curious about the more complicated solutions, if I may take up more of your time. Greetings Loki
1496176248

Edited 1496176403
Lithl
Pro
Sheet Author
API Scripter
One option would be an  Asynchronous Semaphore . Your code would then look something like: function Semaphore(callback, initial, context) { // see linked page for Semaphore implementation } Semaphore.prototype = { // see linked page for Semaphore implementation }; on("change:repeating_superpowers", function(eventinfo) {     var superpowers = [];     superpowers.push("test1"); var sem = new Semaphore(function() { // this code will run when the number of p() calls matches the number of v() calls // we call v() right before calling an async function (getSectionIDs/getAttrs) and // we call p() at the end of the async callback setAttrs({testfield: superpowers.length}); }); sem.v();     getSectionIDs("repeating_superpowers", function(idarray) {         _.each(idarray, function(currentID, i) { sem.v();             getAttrs(["repeating_superpowers_" + currentID +"_spvalue", "repeating_superpowers_" + currentID +"_spname"], function(v) {                 var superpowername = v["repeating_superpowers_" + currentID + "_spname"];                 var superpowervalue = v["repeating_superpowers_" + currentID + "_spvalue"];                 superpowers.push("test2");                 if (superpowername.toLowerCase() == "lasereyes") {                     setAttrs({                         lasereyes: 1                     });                 } else if (superpowername.toLowerCase() == "wings") {                     setAttrs({                         speed: 10                     });                 } sem.p();             });         }); sem.p();     }); }); The semaphore doesn't give you any guarantee about the order your async functions will run, but it will guarantee that the specific function you pass to its constructor will run when all your async tasks are done. (Assuming you place the v() and p() calls in the correct places, of course.)
1496210577
Loki
Sheet Author
Hey Brian. Thanks again for your reply. I'll give it a try. Greetings
1496238359
Loki
Sheet Author
Ok, this technically worked, but in the end I got stuck anyway. Apparently the remove:repeating_ event doesn't fire the change:repeating_-event and since I'm not able to get the value of an attribute of a just removed row of a repeating section (because, well, it's deleted), I can't find a way to achieve this goal (change attributes if a repeating row with a certain attribute value gets deleted). Just for the case that there are other users with a smiliar problem (if you found a solution, I'd glad to hear from you). Greetings Loki
1496250646
Lithl
Pro
Sheet Author
API Scripter
Hmm... you could store a copy of the repeating section's data in some hidden attribute(s) (or even an attribute(s) not actually in the HTML), and compare the contents of the repeating section after the delete to the copy of the data to find out the value that was deleted. (And then, of course, update the data copy to reflect the deletion.)
1496250845
Loki
Sheet Author
Hi. That is indeed a neat idea, which I will try out. Thanks again! Greetings Loki
1496251413

Edited 1496251505
chris b.
Pro
Sheet Author
API Scripter
For these sorts of things we have to have "total" functions that run whenever a row is deleted, so it will loop through all remaining rows and total them up. (or whatever you need to do that depends on the list data) I don't know if having a copy of certain values outside of the list would work. It might and that might be faster. But, besides having to make sure to update those values whenever the value of the row changes, the big problem is sheetworkers have no access to defer or to lock attributes or any way to wait for another function to finish, we can't be sure two different clicks will not clobber each other when updating that single attribute on the sheet  I find when a user clicks too fast, they will fire around the same time, and if i'm updating a single value i can get indeterminate results. So I don't use copies like that, i just recalc the entire list. But the Pathfinder sheet is very slow and huge. A small sheet might be easier. but then again a small sheet can total up a list faster too.
1496300209
Loki
Sheet Author
Hi, chris b. Thank you for your reply! Concerning my character sheet we're talking about 6 oder 7 rows with 2 fields each, so it isn't that much of a deal to list 12-14 row attributes, I guess. What I actually could imagine (simplified): on(change:repeating_superpowers) check if the entry that is made a specific superpower and change the attributes that are linked to that power sum up all entries of the repeating section in an array write the array into a hidden dummy input field on(remove:repeating_superpowers) sum up all entries of the repeating section - again - in an array write this array in a second dummy input field check if there is any entry in the first dummy field that is not in the second (e.g. lasereyes is missing) check if the lasereyes modification is still active - if yes, delete it I'm wondering if it would be useful to write a function that writes the content of a specific repeating section into a specific input field (both may be given as arguments). I'll have to dig into this deeper, because I didn't use html objects/attributes (such as inpu fields) as arguments yet. Greetings, Loki
1496328655
Loki
Sheet Author
Hey guys! Finally it works. I got some problems because when the last row of the repeating section gets deleted, it apparently doesn't fire the remove-event either, but I fixed it with a total wipe of all special attributes if the length of the idarray is 0. Again, thanks for your help! Greetings, Loki