Loop through parallel Layers - javascript

i have a problem with a recursive function. I´ll try to explain as good as possible:
I have a photoshop-document containing various smart-objects all named: Smart_Objectname.
The Smarts may also contain other Smarts.
I now try to write a function that runs trough the whole document and opens a smart(searches with regex for "Smart"), if it contains another it will open the next one and so on. If the last is reached it starts saving each layer inside the smart named with "Layername.png" as png and closes it again. Then it saves the layers of the Smart before until reaching the root document again.
The "going deeper into smarts part" is already working. But if i have two Smart-objects in the same hierarchy it won´t work and set the wrong Layer as active when going deeper into the first one. I simply can´t find the error, but something seems to be wrong with my code.
Any help or easier way to do this would be great :)
Here my code so far...maybe a bit complicated:
findSmart(app.activeDocument.layerSets); //call it
function findSmart(layerNode) {
for (var i=0; i<layerNode.length; i++) {
var res = /Smart/;// a reg exp
var mat = collectNamesAM(res);// get array of matching layer indexes
for( var y = 0; y < mat.length; y++ ){
makeActiveByIndex( mat[y], false );
// do something with layer
var activeLayer = app.activeDocument.activeLayer;
//Open smart
//=======================================================
var idplacedLayerEditContents = stringIDToTypeID( "placedLayerEditContents" );
var desc45 = new ActionDescriptor();
executeAction( idplacedLayerEditContents, desc45, DialogModes.NO );
}//next Smart match if any
//Go deeper
findSmart(layerNode[i].layerSets);
var len = app.activeDocument.layers.length;
for (var i = 0; i < len; i++) {
var layer = app.activeDocument.layers[i];
//search for all .png Layers to save
var id = ".png";
var exist = layer.name.slice(-id.length) == id; // true
app.activeDocument.activeLayer = layer;
var activeLayer = app.activeDocument.activeLayer;
//save pic if .png
if(exist){
saveAll(); //just opens each layer in another doc and saves it as png then closes the documents without saving
}//end if
}//end for
// Close the Smart Object and move to next one
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}//first smart
}//end function

I found the solution. it was just a little mistake with the call of the recursive part(i did it too early) and an if that checks for a smart inside a smart. so the last bit of the code right after the if(exist) thing should be:
if(y == 0){
findSmart(layerNode[y].layerSets);
}
//Close the Smart Object and move to next one
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}//next smart if any
}//end for
}//end function

Related

Photoshop scripting with JSX in for loop to change fill color of each layer

So, I am tinkering with changing fill colors of layers in a PSD file using JSX. I ultimately want to loop all layers, turning off visibility for all but one, edit the fill color of that layer, save as PNG, and then repeat for all layers and all colors in JSON file. I'm starting small as this is my first attempt, but if your solution can help preempt my downfalls with other tasks then it would be greatly appreciated. Here is what I have (alert properly prompts, but line 5 receives error 1302: no such element referencing line 5):
var layerNum = app.activeDocument.layers.length
alert(layerNum);
var i;
for (i=0;i<layerNum;i++){
var currentLayer = app.activeDocument.layers.index(i)
var myColor = new SolidColor();
//var RGB = HEXtoRGB(Y);
myColor.rgb.red = RGB[255];
myColor.rgb.green = RGB[0];
myColor.rgb.blue = RGB[0];
currentLayer.fill.color = myColor;
}
Is this because the collection of layers does not start at 0? Should I start with layers.index(layerNum) and use i-- to move down the collection? Any insight would be helpful. Thanks in advance to this always helpful community.
This var currentLayer = app.activeDocument.layers.index(i)
should be this: var currentLayer = app.activeDocument.layers[i]
Layers collections are pretty much the same as arrays, starting from 0, so you did it right
As KienT points out you need to use square brackets for layers.
Also it's useful to create a variable that's the app.activeDocument, so you don't have to type that out each time. Looping over the layers backwards (or up) is easy - just remember to take 1 off the layers length. You can adjust also make it ignore the background by making the loop start at 1 instead of 0. You can also adjust the layers visibility from true to false (on and off) as you go.
// call the source document
var srcDoc = app.activeDocument;
var layerNum = srcDoc.layers.length;
// alert(layerNum);
for (var i = layerNum -1; i >= 0; i--)
{
var currentLayer = srcDoc.layers[i];
var myColor = new SolidColor;
// Select the layers as you go
srcDoc.activeLayer = srcDoc.artLayers[i];
// switch layer visibility to on
srcDoc.visible = true;
myColor.rgb.red = 255;
myColor.rgb.green = 0;
myColor.rgb.blue = 0;
app.activeDocument.selection.fill(myColor, ColorBlendMode.NORMAL, 100, false);
}

Loop reload same trial instead of next one Qualtrics

I have a problem with Qualtrics Loop and Merge + Javascript. Basically, Qualtrics reload the previous trial of my loop instead of a new one.
First, here is what I try to do: I have two sets of pictures, at each loop, one picture of each set is randomly picked and displayed (randomly to the left or right side). At the end of the loop, each pictures of the set will be displayed (33 in each set, so 33 trials of the loop), without picking twice the same picture. At each trial, I also display randomly the name of the condition "in relationship" or "friends"
The problem in details:
I have coded something that seems to work quite well, except at some point: It happens that when loading the next trial of the loop, it doesn't display new pictures but instead the one that were presented in the previous trial (and also the same name of condition). This can go for a while presenting me always the same pictures, or this can just be one time.
In any case, this trial which is represented count as a trial, as my loop always ends after 33 trials, whatever if some of these are actually not new pictures presented.
This problem doesn't appear always at the same trial, and can appear several times in a loop.
It also happened that only one picture displayed is new, and the other is the one presented in the previous trial. (I really don't get how this can happen).
I coded the randomisation of picture in an empty question, just before the question where it displays the pictures. This empty question is supposed to not require to press the "next" button. When the trial are correct, this is working well, but when the next trial is going to be one "reloaded" trial, it requires to press the "next" button.
I tried the survey with Firefox and Edge, and the problem is the same.
My javascript code:
In the first question of the survey:
Qualtrics.SurveyEngine.addOnload(function () {
function shuffle(o) { //v1.0
for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
var FacesM_list = "IM_7U04c3TQlA7dT3D,IM_eE5iBcKfoOblbJH,IM_a3mklzA9E1OuRWl,IM_bQnoSJOGwa0vn9P,IM_5inNCVgPdHHTVSR,IM_bHlurQJWSXDRDFj,IM_3rCr2DIzW2cvqLP,IM_509x1wz7pM6PP0N,IM_3UGdsp02IcCqaSV,IM_eLrY7bwDPiT7apn,IM_3LdlHnBb6tnkBEh,IM_3pY6z6JDgaDvwq1,IM_9HtZxBS79DiOfR3,IM_03c9pDSpcwcqIyF,IM_6WGKJOzUWK4TJat,IM_2rxQPEGO7SEvsY5,IM_9YN3UCUtWEWTfGR,IM_8jZTSUAGJfuVECV,IM_9nQkFhRY2cLIVgN,IM_2abcJA7B79jt30h,IM_cD31N8XPTGliUN7,IM_0eL8iQd4PVdyuQl,IM_cuOoV9gSAe6CWd7,IM_9Nv3X7lWYEsTzsF,IM_5ccXAuuomEEyamp,IM_9mnvThFiNA5U84t,IM_e3UGNNuMdrKH8cl,IM_3aggsd5P9MMlUDr,IM_4ORY6GEMW8CmNPT,IM_50WOBkz8ADTFGHH,IM_3rqtlVBfijYCccZ,IM_3CzDsr0tYv7PH5H,IM_4SmeprjDgOeCl5b"
var FacesF_list = "IM_6fkHDs5f6ItAuk5,IM_0ri9MLjDhHxyonP,IM_bKlHtoAaxnBFlnT,IM_1WUqtBPpdhERpjf,IM_ac0yWos8tAqSMNT,IM_3xCePACn1Lq97tH,IM_6o1ZPLGUM682Au1,IM_babATdN3VtBLIsl,IM_8HSUICLvFvDXaN7,IM_0ebTztq3ML5Zh0V,IM_3lB8j5dhMs8i8ip,IM_0iC0pwDlpOkcTGt,IM_cIRojwU6sx3W7Od,IM_9ZHNbignrAfcThX,IM_8iFXvVcCqk5hemN,IM_6rrwImdl4Nss0u1,IM_6mPEaoIdazwqAWp,IM_b8lrxhsPGcc1HaR,IM_23uYWeF2gYVMsap,IM_6ycfrm5xOlewjFb,IM_7UKValFGc9Kmpp3,IM_8Bbkzsmc7CyMvqt,IM_d5S95FnSgo8j06F,IM_brXT4VUU8QJiRwN,IM_9MEkpgEmOwXhril,IM_6KG9qokOlD16GDH,IM_ellgVnYbtb8ZSbb,IM_eg6qSYMQ56z5JpX,IM_5vfbDNPdZeP1XCZ,IM_cDbOprwCUUSnUZT,IM_cumIGHXOFByV4Pz,IM_0jh2Va4JTfGsQDz,IM_0CGlFRy4dW8lDcF"
var FacesM_order = [];
for (var i = 1; i <= 33; i++) {
FacesM_order.push(i);
}
FacesM_order = shuffle(FacesM_order);
var FacesF_order = [];
for (var i = 1; i <= 33; i++) {
FacesF_order.push(i);
}
FacesF_order = shuffle(FacesF_order);
var nTrial = 0;
Qualtrics.SurveyEngine.setEmbeddedData("FacesM_order", FacesM_order.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesF_order", FacesF_order.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesF_list", FacesF_list.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesM_list", FacesM_list.toString());
Qualtrics.SurveyEngine.setEmbeddedData("nTrial", nTrial.toString());
});
And in the first question of the Block with Loop and Merge:
Qualtrics.SurveyEngine.addOnload(function () {
var nTrial = Number("${e://Field/nTrial}") + 1;
var FacesF_list = "${e://Field/FacesF_list}".split(',');
var FacesM_list = "${e://Field/FacesM_list}".split(',');
var FacesF_order = "${e://Field/FacesF_order}".split(',');
var FacesM_order = "${e://Field/FacesM_order}".split(',');
var FacesF = FacesF_list[FacesF_order[nTrial]];
var FacesM = FacesM_list[FacesM_order[nTrial]];
var rand = Math.random()
if (rand < 0.5) {
var left = FacesF
var right = FacesM
} else {
var left = FacesM
var right = FacesF
}
Qualtrics.SurveyEngine.setEmbeddedData("nTrial", nTrial.toString());
Qualtrics.SurveyEngine.setEmbeddedData("left", left.toString());
Qualtrics.SurveyEngine.setEmbeddedData("right", right.toString());
this.clickNextButton.delay(.00000000000001);
});
Thanks a lot for your help!

InDesign script: footnotes loosing their style

Ok, I know this had been already discussed here but no clear answer had been provided. I often need to import XML files into InDesign, many footnotes included. Of course, InD is not able to use tags automatically in this case. This script works well except all footnotes loose their styles. I know it could be because of the contents on lines 27 and 35. It is needed to use move instead. Unfortunately, I am not good at JavaScript and can't figure out how to implement that properly.
Application.prototype.main = function(){
if ( this.documents.length <= 0 ) return;
var tg = this.selection[0] || this.activeDocument;
if( 'appliedFont' in tg ) tg = tg.parent;
if( tg.constructor == TextFrame ){ tg = tg.parentStory ; }
if(! ('findGrep' in tg) ) return;
var fnPatterns = ["#foot#([\\s\\S]*?)#foot#", "#footnotes_begin#([\\s\\S]*?)#footnotes_end#"];
var count = 0;
for(patterCounter = 0; patterCounter < fnPatterns.length; patterCounter++){
fnPattern = fnPatterns[patterCounter];
var fnFinds = (function(){
this.findGrepPreferences = this.changeGrepPreferences = null;
this.findGrepPreferences.findWhat = fnPattern;
var ret = tg.findGrep();
this.findGrepPreferences = this.changeGrepPreferences = null;
return ret;
}).call(this);
var fnFind, fnText, rg = new RegExp(fnPattern), ip, fnParent, fn, count;
while( fnFind=fnFinds.pop() ){
fnText = fnFind.contents.match(rg)[1];
fnParent = fnFind.parent.getElements()[0];
ip = fnFind.insertionPoints[0].index
try {
fnFind.remove();
fn = fnParent.footnotes.add(LocationOptions.BEFORE, fnParent.insertionPoints[ip]);
fn.texts[0].insertionPoints[-1].contents = fnText;
++count;
}
catch(_){}
}
}
alert((count)? (count+" footnote(s) successfully added."): "No footnote added. Make sure you use the relevant pattern.");
}
app.doScript('app.main();', ScriptLanguage.javascript,
undefined, UndoModes.entireScript, app.activeScript.displayName);
The problem is exactly the same as in the question you linked to: you are manipulating a plain string translation in Javascript, not the native InDesign text object itself. Use move and duplicate methods on the text property of the found list instead.
A basic solution is to use
fn = fnFind.footnotes.add(LocationOptions.AFTER, fnFind.insertionPoints[-1]);
fnFind.texts[0].move (LocationOptions.AT_END, fn.texts[0]);
but this will copy the start and end markers as well. It takes a bit more to remove them; I based the following adjustments in your original script on your GREP patterns, but it may be safer to build an explicit list of prefix/suffix pairs, as you also can use them to construct the GREP searches.
The next problem, then, is if you copy (duplicate, in InDesign's DOM) the found text, the original "found" text will now have the footnote attached to it! This is because one line earlier, you add the footnote to the "found" text. So you cannot use a simple remove to delete it; again, you need to manipulate the text object, but this time through its individual characters. My last adjusted line 'selects' the fnFind texts, minus its very last character (which is the newly added footnote), and deletes it.
var fnFind, fnPrefix,fnSuffix, rg = new RegExp(fnPattern), ip, fnParent, fn, count;
while( fnFind=fnFinds.pop() ){
fnPrefix = fnFind.contents.match(/^#[^#]+#/)[0];
fnSuffix = fnFind.contents.match(/#[^#]+#/)[0];
// add footnote
fn = fnFind.footnotes.add(LocationOptions.AFTER, fnFind.insertionPoints[-1]);
// duplicate the text
fnFind.texts[0].characters.itemByRange(fnPrefix.length,fnFind.texts[0].characters.length-fnSuffix.length-1).duplicate(LocationOptions.AT_END, fn.texts[0]);
// remove the original
fnFind.characters.itemByRange(0,fnFind.characters.length-2).remove();
++count;
}

Get array values and put them into different divs with a for loop?

I'm trying hard to learn javascrip+jquery on my own, and also trying to learn it right. Thus trying to enforce the DRY rule.
I find myself stuck with a problem where I have an array,
var animals = [4];
a function,
var legs = function(amount){
this.amount = amount;
this.body = Math.floor(Math.random()*amount)+1;
}
and an evil for loop. I also have 5 div's called printAnimal1, printAnimal2 and so on.. In which I wish to print out each value in the array into.
for(i = 0; i < animals.length; i++){
animals[i] = new legs(6);
$(".printAnimal"+i).append("animals[i]");
}
I feel as if I'm close to the right thing, but I cant seem to figure it out. I also tried something like this:
for(i = 0; i < animals.length; i++){
animals[i] = new legs(6);
$this = $(".printAnimal");
$(this+i).append("animals[i]");
}
But one of the problems seem to be the "+i" and I cant make heads or tails out of it.
I also know that I can simply do:
$(".printAnimal1").append("animals[i]");
$(".printAnimal2").append("animals[i]");
$(".printAnimal3").append("animals[i]");
...
But that would break the DRY rule. Is it all wrong trying to do this with a for loop, or can it be done? Or is there simply a better way to do it! Could anyone clarify?
Your first attempt should be fine, as long as you take "animals[i]" out of quotes in your append() call ($(".printAnimal"+i).append(animals[i]))
Also, I assume you declared var i; outside your for loop? If not, you'll want to declare it in your for loop (for(var i=0....)
EDIT: problems with your fiddle
you never call startGame()
you didn't include jQuery
you can't (as far as I know) append anything that isn't html-- in your case, you're trying to append a js object. What do you want the end result to look like?
http://jsfiddle.net/SjHgh/1/ is a working fiddle showing that append() works as you think it should.
edit: forgot to update the fiddle. Correct link now.
EDIT: reread your response to the other answer about what you want. http://jsfiddle.net/SjHgh/3/ is a working fiddle with what you want. More notes:
You didn't declare new when you called DICE
you have to reference the field you want, (hence dices[i].roll), not just the object
Just a few comments:
This is declaring an array with only one item and that item is the number 4
var animals = [4];
In case you still need that array, you should be doing something like:
var animals = []; // A shiny new and empty array
and then add items to it inside a for loop like this:
animals.push(new legs(6)); //This will add a new legs object to the end of the array
Also, what is the content that you are expecting to appear after adding it to the div?
If you want the number of legs, you should append that to the element (and not the legs object directly).
for(i = 0; i < animals.length; i++){
animals.push(new legs(6));
$(".printAnimal"+i).append(animals[i].body);
}
Adding another answer as per your comment
var i, dicesThrown = [];
function throwDice(){
return Math.ceil(Math.random() * 6);
}
//Throw 5 dices
for (i=0 ; i<5 ; i++){
dicesThrown.push( throwDice() );
}
//Show the results
for (i=0 ; i<5 ; i++){
$("body").append("<div>Dice " + (i+1) + ": " + dicesThrown[i] +"</div>");
}

Virtual Earth VEShapeLayer Will Not Render

The goal: Allow user to turn on and off different layers of data; and to dynamically pull the data for the current extent from a database on map move event.
This works fine and good if you hard code your VEShapeLayers as done here.
My list of layers is coming from a database, I have everything working the way I want except that when I add shapes to my VEShapeLayer none are rendered on my map. Calling VEShapeLayer.GetShapeCount() returns the expected number... so the layer has the data..
Here are the important bits of the code:
var assets = [];
if (WebServiceResult.length > 0) {
for (var i = 0; i < WebServiceResult.length; i++) {
var ix = FindLayerIndex(WebServiceResult[0].AssetMapLayer);
var velatlong = new VELatLong();
velatlong.Latitude = WebServiceResult[i].Latitude;
velatlong.Longitude = WebServiceResult[i].Longitude;
newShape = new VEShape(VEShapeType.Pushpin, velatlong);
assets.push(newShape);
}
// ix is defined above and is vaild and correct
map.GetShapeLayerByIndex(ix).AddShape(assets);
}
// a call here to map.GetShapeLayerByIndex(ix).GetShapeCount()
// returns the expected number of shapes
I feel dumb; I had not added the stylesheet to my page's header, and my custom icon was rendering transparent as a result of the stylesheet not being available.

Categories