• Global community
    • Language:
      • Deutsch
      • English
      • EspaƱol
      • FranƧais
      • PortuguĆŖs
  • ę—„ęœ¬čŖžć‚³ćƒŸćƒ„ćƒ‹ćƒ†ć‚£
    Dedicated community for Japanese speakers
  • ķ•œźµ­ ģ»¤ė®¤ė‹ˆķ‹°
    Dedicated community for Korean speakers
Exit
1

Photoshop script that loop through layersets and creates new one based on layers name

Community Beginner ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Hi! I'm new with scripting and can't figure out how to make one that works for me.

Inside my psd file i have Groups with layers named *shutterstock*. I need to:
For every layerset:
1. Select *shutterstock* layer and every layer below until selection reach to another *shutterstock* layer

2. Group this selection and give it the name of Parent LayerSet.
3.Move created group from Parent LayerSet.

4. Continue loop with another *shutterstock* layer and so on
I have attached screenshot to visualize my goal.
At that point i figgured out how to select *shutterstock* layers:

if (app.documents.length > 0) {
    // the file;
    var doc = app.activeDocument;
    // get number of layers;
    var ref = new ActionReference();
    ref.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
    var applicationDesc = executeActionGet(ref);
    var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
    for (var m = 0; m <= theNumber; m++) {
        try {
            var ref = new ActionReference();
            ref.putIndex(charIDToTypeID("Lyr "), m);
            var layerDesc = executeActionGet(ref);
            var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
            var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
            // if not layer group collect values;
            if (layerSet != "layerSectionEnd" /*&& layerSet != "layerSectionStart"*/ && isBackground != true) {
                var theName = layerDesc.getString(stringIDToTypeID('name'));
                var theColor = layerDesc.getEnumerationValue(stringIDToTypeID("color"));
                var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
                var visible = layerDesc.getBoolean(stringIDToTypeID("visible"));
                if ((theName).match(/shutterstock/)) {

                    var desc = new ActionDescriptor()
                    var ref = new ActionReference()
                    ref.putIndex(stringIDToTypeID("layer"), m)
                    desc.putReference(stringIDToTypeID("null"), ref)
                    desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("addToSelectionContinuous"), stringIDToTypeID("addToSelection"))
                    //desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelectionContinuous"))
                    executeAction(stringIDToTypeID("select"), desc, DialogModes.NO);
                }
            };
        }
        catch (e) { };
    };
};

 

I can't figure out how to select other layers and group them.
It should be some kind of list or array? 

From scripting listener i made some draft but i can't finalize it šŸ˜ž

var idselect = stringIDToTypeID( "select" );
    var desc386 = new ActionDescriptor();
    var idnull = stringIDToTypeID( "null" );
        var ref27 = new ActionReference();
        var idlayer = stringIDToTypeID( "layer" );
        ref27.putName( idlayer, "bow_4" );
    desc386.putReference( idnull, ref27 );
    var idselectionModifier = stringIDToTypeID( "selectionModifier" );
    var idselectionModifierType = stringIDToTypeID( "selectionModifierType" );
    var idaddToSelectionContinuous = stringIDToTypeID( "addToSelectionContinuous" );
    desc386.putEnumerated( idselectionModifier, idselectionModifierType, idaddToSelectionContinuous );
    var idmakeVisible = stringIDToTypeID( "makeVisible" );
    desc386.putBoolean( idmakeVisible, false );
    var idlayerID = stringIDToTypeID( "layerID" );
        var list23 = new ActionList();
        list23.putInteger( 3340 );
        list23.putInteger( 3341 );
        list23.putInteger( 3342 );
        list23.putInteger( 3343 );
    desc386.putList( idlayerID, list23 );
executeAction( idselect, desc386, DialogModes.NO );
var desc = new ActionDescriptor()
                    var ref = new ActionReference()
                    ref.putIndex(stringIDToTypeID("layer"), m)
                    desc.putReference(stringIDToTypeID("null"), ref)
                    desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("addToSelectionContinuous"), stringIDToTypeID("addToSelection"))
                    //desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelectionContinuous"))
                    var idmake = stringIDToTypeID("make");
                    var idnull = stringIDToTypeID("null");
                    var idlayerSection = stringIDToTypeID("layerSection");
                    var idfrom = stringIDToTypeID("from");
                    var idlayer = stringIDToTypeID("layer");
                    var idordinal = stringIDToTypeID("ordinal");
                    var idtargetEnum = stringIDToTypeID("targetEnum");
                    var idcolor = stringIDToTypeID("color");
                    var idred = stringIDToTypeID("red");
                    var idlayerSection = stringIDToTypeID("layerSection");
                    var idusing = stringIDToTypeID("using");
                    var idname = stringIDToTypeID("name");
                    var idlayerSectionStart = stringIDToTypeID("layerSectionStart");
                    var idlayerSectionEnd = stringIDToTypeID("layerSectionEnd");

                    var desc1695 = new ActionDescriptor();
                    var ref155 = new ActionReference();
                    ref155.putClass(idlayerSection);
                    desc1695.putReference(idnull, ref155);
                    var ref156 = new ActionReference();
                    ref156.putEnumerated(idlayer, idordinal, idtargetEnum);
                    desc1695.putReference(idfrom, ref156);
                    var desc1696 = new ActionDescriptor();
                    //desc1696.putEnumerated(idcolor, idcolor, idred);
                    desc1695.putObject(idusing, idlayerSection, desc1696);
                    desc1695.putInteger(idlayerSectionStart, 3839);
                    desc1695.putInteger(idlayerSectionEnd, 3840);
                    desc1695.putString(idname, """Group 1""");
                    //executeAction(idmake, desc1695, DialogModes.NO);
                    executeAction(stringIDToTypeID("select"), desc, DialogModes.NO);

 

 

 

 

 

Screenshot 2023-03-10 at 11.25.16.pngScreenshot 2023-03-10 at 11.27.04.pngScreenshot 2023-03-10 at 11.28.01.pngScreenshot 2023-03-10 at 11.28.32.png

TOPICS
Actions and scripting

Views

1.3K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Mar 10, 2023 Mar 10, 2023

Does this help? 

Screenshot 2023-03-10 at 12.04.36.pngScreenshot 2023-03-10 at 12.04.46.png

 

// group layers beneath "shutterstock_xxx"-layer within group;
// 2023, use it at your own risk;
if (app.documents.length > 0) {
    var thisName = "shutterstock";
    var theLayers = collectLayersNamesIndexAndIdentifier(thisName);
    var theCheck = false;
    for (var m  = theLayers.length - 1; m >= 0; m--) {
        if (theLayers[m][3] == "layerSectionStart") {var groupName = theLayers[m][0]};
        if (theLayers[m][3] == "layerSectionEnd") {
            if (theCheck == 
...

Votes

Translate

Translate
Adobe
Community Expert ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Does this help? 

Screenshot 2023-03-10 at 12.04.36.pngScreenshot 2023-03-10 at 12.04.46.png

 

// group layers beneath "shutterstock_xxx"-layer within group;
// 2023, use it at your own risk;
if (app.documents.length > 0) {
    var thisName = "shutterstock";
    var theLayers = collectLayersNamesIndexAndIdentifier(thisName);
    var theCheck = false;
    for (var m  = theLayers.length - 1; m >= 0; m--) {
        if (theLayers[m][3] == "layerSectionStart") {var groupName = theLayers[m][0]};
        if (theLayers[m][3] == "layerSectionEnd") {
            if (theCheck == true) {
                groupSelectedLayers(groupName);
                deselectLayers()
            };
            theCheck = false
        };
        if (theLayers[m][0].match(thisName) && theLayers[m][3] == "layerSectionContent") {
            if (theCheck == true) {
                groupSelectedLayers(groupName);
                deselectLayers();
            };
            selectLayerByID(theLayers[m][2],false);
            theCheck = true;
        } else {
            if (theCheck == true) {selectLayerByID(theLayers[m][2],true)};
        };
    };
};
////// collect layers with certain name //////
function collectLayersByName (aName) {
// the file;
var myDocument = app.activeDocument;
// get number of layers;
var ref = new ActionReference();
ref.putProperty(stringIDToTypeID('property'), stringIDToTypeID('numberOfLayers'));
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if group collect values;
if (layerSet != "layerSectionEnd" /*&& layerSet != "layerSectionStart" && isBackground != true*/) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));
if (theName == aName) {theLayers.push([theName, theIndex, theID])}
};
}
catch (e) {};
};
return theLayers
};
////// collect layers //////
function collectLayersNamesIndexAndIdentifier () {
// the file;
    var myDocument = app.activeDocument;
// get number of layers;
    var ref = new ActionReference();
    ref.putProperty(stringIDToTypeID('property'), stringIDToTypeID('numberOfLayers'));
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    var applicationDesc = executeActionGet(ref);
    var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
    var theLayers = new Array;
    for (var m = 0; m <= theNumber; m++) {
    try {
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID( "Lyr " ), m);
    var layerDesc = executeActionGet(ref);
    var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
    var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if group collect values;
    //if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" /*&& isBackground != true*/) {
    var theName = layerDesc.getString(stringIDToTypeID('name'));
    var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
    var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));
    theLayers.push([theName, theIndex, theID, layerSet])
    //};
    }
    catch (e) {};
    };
    return theLayers
    };
////// based on code by mike hale, via paul riggott //////
function selectLayerByID(id,add){ 
    add = undefined ? add = false:add 
    var ref = new ActionReference();
        ref.putIdentifier(charIDToTypeID("Lyr "), id);
        var desc = new ActionDescriptor();
        desc.putReference(charIDToTypeID("null"), ref );
           if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
          desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
       try{
        executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
    }catch(e){
    alert(e.message); 
    }
    };
////// group //////
function groupSelectedLayers (theName) {
    var desc159 = new ActionDescriptor(); 
    var ref114 = new ActionReference(); 
    var idlayer = stringIDToTypeID( "layer" );
    var idordinal = stringIDToTypeID( "ordinal" );
    var idtargetEnum = stringIDToTypeID( "targetEnum" );
    var idnull = stringIDToTypeID( "null" );
    var idname = stringIDToTypeID( "name" );
    ref114.putEnumerated(idlayer, idordinal, idtargetEnum); 
    desc159.putReference(idnull, ref114 );
    desc159.putString(idname, "aaa" );
    executeAction( stringIDToTypeID( "groupLayersEvent" ), desc159, DialogModes.NO );
    var desc63 = new ActionDescriptor();
    var ref37 = new ActionReference();
    ref37.putEnumerated( idlayer, idordinal, idtargetEnum );
    desc63.putReference( idnull, ref37 );
    var desc64 = new ActionDescriptor();
    desc64.putString(idname, theName);
    desc63.putObject(stringIDToTypeID( "to" ), idlayer, desc64);
    executeAction(stringIDToTypeID( "set" ), desc63, DialogModes.NO)
};
////// deselect layers //////
function deselectLayers() { 
    var desc01 = new ActionDescriptor(); 
        var ref01 = new ActionReference(); 
        ref01.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') ); 
    desc01.putReference( charIDToTypeID('null'), ref01 ); 
    executeAction( stringIDToTypeID('selectNoLayers'), desc01, DialogModes.NO ); 
    };

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Many many thanks!
It works perfect!
One more thing, is there a way to move created groups from parent group? On your Screenshot example in the end it should be two group named 'aaa' with Shutterstock layers inside and one empty 'aaa' group from which that two groups been moved.

Screenshot 2023-03-10 at 15.13.32.png

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Ah, I had missed that part ā€¦ I canā€™t deny that my Forum-reading is often sloppy. 

 

// group layers beneath "shutterstock_xxx"-layer within group and move atop group;
// 2023, use it at your own risk;
if (app.documents.length > 0) {
    var thisName = "shutterstock";
    var theLayers = collectLayersNamesIndexAndIdentifier(thisName);
    var theCheck = false;
    for (var m  = theLayers.length - 1; m >= 0; m--) {
        if (theLayers[m][3] == "layerSectionStart") {
            var groupName = theLayers[m][0];
            var theGroupId = theLayers[m][2]
        };
        if (theLayers[m][3] == "layerSectionEnd") {
            if (theCheck == true) {
                groupSelectedLayers(groupName);
                moveLayer (theLayers[m][2], getLayerIndex(theGroupId)+1);
                deselectLayers()
            };
            theCheck = false
        };
        if (theLayers[m][0].match(thisName) && theLayers[m][3] == "layerSectionContent") {
            if (theCheck == true) {
                groupSelectedLayers(groupName);
                moveLayer (theLayers[m][2], getLayerIndex(theGroupId)+1);
                deselectLayers();
            };
            selectLayerByID(theLayers[m][2],false);
            theCheck = true;
        } else {
            if (theCheck == true) {selectLayerByID(theLayers[m][2],true)};
        };
    };
};
////// collect layers with certain name //////
function collectLayersByName (aName) {
// the file;
var myDocument = app.activeDocument;
// get number of layers;
var ref = new ActionReference();
ref.putProperty(stringIDToTypeID('property'), stringIDToTypeID('numberOfLayers'));
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if group collect values;
if (layerSet != "layerSectionEnd" /*&& layerSet != "layerSectionStart" && isBackground != true*/) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));
if (theName == aName) {theLayers.push([theName, theIndex, theID])}
};
}
catch (e) {};
};
return theLayers
};
////// collect layers //////
function collectLayersNamesIndexAndIdentifier () {
// the file;
    var myDocument = app.activeDocument;
// get number of layers;
    var ref = new ActionReference();
    ref.putProperty(stringIDToTypeID('property'), stringIDToTypeID('numberOfLayers'));
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    var applicationDesc = executeActionGet(ref);
    var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
    var theLayers = new Array;
    for (var m = 0; m <= theNumber; m++) {
    try {
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID( "Lyr " ), m);
    var layerDesc = executeActionGet(ref);
    var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
    var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if group collect values;
    //if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" /*&& isBackground != true*/) {
    var theName = layerDesc.getString(stringIDToTypeID('name'));
    var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
    var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));
    theLayers.push([theName, theIndex, theID, layerSet])
    //};
    }
    catch (e) {};
    };
    return theLayers
    };
////// based on code by mike hale, via paul riggott //////
function selectLayerByID(id,add){ 
    add = undefined ? add = false:add 
    var ref = new ActionReference();
        ref.putIdentifier(charIDToTypeID("Lyr "), id);
        var desc = new ActionDescriptor();
        desc.putReference(charIDToTypeID("null"), ref );
           if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
          desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
       try{
        executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
    }catch(e){
    alert(e.message); 
    }
    };
////// group //////
function groupSelectedLayers (theName) {
    var desc159 = new ActionDescriptor(); 
    var ref114 = new ActionReference(); 
    var idlayer = stringIDToTypeID( "layer" );
    var idordinal = stringIDToTypeID( "ordinal" );
    var idtargetEnum = stringIDToTypeID( "targetEnum" );
    var idnull = stringIDToTypeID( "null" );
    var idname = stringIDToTypeID( "name" );
    ref114.putEnumerated(idlayer, idordinal, idtargetEnum); 
    desc159.putReference(idnull, ref114 );
    desc159.putString(idname, "aaa" );
    executeAction( stringIDToTypeID( "groupLayersEvent" ), desc159, DialogModes.NO );
    var desc63 = new ActionDescriptor();
    var ref37 = new ActionReference();
    ref37.putEnumerated( idlayer, idordinal, idtargetEnum );
    desc63.putReference( idnull, ref37 );
    var desc64 = new ActionDescriptor();
    desc64.putString(idname, theName);
    desc63.putObject(stringIDToTypeID( "to" ), idlayer, desc64);
    executeAction(stringIDToTypeID( "set" ), desc63, DialogModes.NO)
};
////// deselect layers //////
function deselectLayers() { 
    var desc01 = new ActionDescriptor(); 
        var ref01 = new ActionReference(); 
        ref01.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') ); 
    desc01.putReference( charIDToTypeID('null'), ref01 ); 
    executeAction( stringIDToTypeID('selectNoLayers'), desc01, DialogModes.NO ); 
    };
////// move active layer to //////
function moveLayer (thisLayerId, theId) {
var idlayer = stringIDToTypeID( "layer" );
    var desc58 = new ActionDescriptor();
        var ref19 = new ActionReference();
        ref19.putEnumerated( idlayer, stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
    desc58.putReference( stringIDToTypeID( "null" ), ref19 );
        var ref20 = new ActionReference();
        ref20.putIndex( idlayer, theId );
    desc58.putReference( stringIDToTypeID( "to" ), ref20 );
    desc58.putBoolean( stringIDToTypeID( "adjustment" ), false );
    desc58.putInteger( stringIDToTypeID( "version" ), 5 );
        var list11 = new ActionList();
        list11.putInteger(thisLayerId);
    desc58.putList( stringIDToTypeID( "layerID" ), list11 );
executeAction( stringIDToTypeID( "move" ), desc58, DialogModes.NO );
};
////// by mike hale, via paul riggott //////
function getLayerIndex(theId){
    var ref = new ActionReference(); 
    ref.putProperty(stringIDToTypeID('property'), stringIDToTypeID('itemIndex'));
    ref.putIdentifier(charIDToTypeID("Lyr "), theId); 
    d = executeActionGet(ref);
    return (d.getInteger(stringIDToTypeID('itemIndex'))-1); 
    };

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Now, if above created Groups ( 2x'aaa') exist another Group ('bbb'), script will put this Groups inside, not atop šŸ˜ž

 

Screenshot 2023-03-10 at 17.32.40.png

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Works fine in my test, can you provide the file? 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Mar 10, 2023 Mar 10, 2023

Copy link to clipboard

Copied

Yes

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 11, 2023 Mar 11, 2023

Copy link to clipboard

Copied

I had made a mistake regarding Layer Indices that only becomes relevant when there is no Background Layer. 

I updated the code, please try again. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Mar 11, 2023 Mar 11, 2023

Copy link to clipboard

Copied

LATEST

Thank you so much! It works perfect!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines