# JavaScript Features

KaMenu includes a powerful JavaScript engine built in (based on OpenJDK Nashorn 15.3), allowing you to use JavaScript code in menus to perform various complex operations.

***

## ✨ Features

* ✅ **Ready to use**: No extra plugins required
* ✅ **Automatic download**: Required dependencies are automatically downloaded on first startup
* ✅ **Bukkit API integration**: Direct access to the Bukkit API
* ✅ **Variable binding**: Automatically binds player-related variables
* ✅ **Predefined functions**: Supports defining reusable JavaScript functions in menus
* ✅ **Parameter passing**: Supports passing multiple types of parameters to predefined functions

***

## 🔧 Basic Usage

### Execute JavaScript code directly

in `actions` using `js:` prefix to execute JavaScript code directly:

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&aTest JS'
    actions:
      - 'js: player.sendMessage("Hello from JavaScript!");'
      - 'js: var random = Math.floor(Math.random() * 100);'
      - 'js: player.sendMessage("Random number: " + random);'
```

***

## 📦 Built-in Variables

The following variables can be used directly in JavaScript code:

| Variable name | Type      | Description                |
| ------------- | --------- | -------------------------- |
| `player`      | Player    | Current player object      |
| `uuid`        | String    | Player UUID string         |
| `name`        | String    | Player name                |
| `location`    | Location  | Player location            |
| `inventory`   | Inventory | Player inventory           |
| `world`       | World     | The world the player is in |
| `server`      | Server    | Server instance            |

**Example:**

```yaml
actions:
  - 'js: player.sendMessage("Welcome, " + name + "!");'
  - 'js: player.sendMessage("UUID: " + uuid);'
  - 'js: player.sendMessage("World: " + world.getName());'
```

***

## 🔌 Built-in Helper Functions

The following helper functions are preconfigured in the JavaScript environment:

### `tell(player, message)`

Send a message to the player

```javascript
tell(player, "Hello!");
```

### `log(message)`

Print a log in the console

```javascript
log("The player clicked the button");
```

### `delay(ticks, callback)`

Execute the callback function after a delay (main thread)

```javascript
delay(20, function() {
    player.sendMessage("Executed after 1 second");
});
```

### `asyncDelay(ticks, callback)`

Execute the callback function asynchronously after a delay

```javascript
asyncDelay(20, function() {
    log("Asynchronous task completed");
});
```

### `getPlayer(name)`

Get a player by name

```javascript
var target = getPlayer("Steve");
if (target) {
    tell(target, "Hello!");
}
```

***

## 📝 Predefined Functions

### Define predefined functions

Add a top-level `JavaScript` node in the menu configuration file to define reusable JavaScript code blocks:

```yaml
Title: '&6JavaScript Example Menu'

# JavaScript predefined function section
JavaScript:
  show_health: |
    var health = player.getHealth();
    var maxHealth = player.getMaxHealth();
    player.sendMessage("§eHealth: §f" + health + "/" + maxHealth);

  greet: |
    player.sendMessage("§aHello, " + name + "!");

  pass_args: |
    var playerName = args[0];
    var playerLevel = args[1];
    var dataValue = args[2];
    var inputValue = args[3];

    player.sendMessage("§aHello, " + playerName + "!");
    player.sendMessage("§aLevel: §f" + playerLevel);
    player.sendMessage("§aData: §f" + dataValue);
    player.sendMessage("§aInput: §f" + inputValue);

Body:
  ...
```

{% hint style="info" %}
`JavaScript` Each key in the node is a function name, and the value is JavaScript code (using `|` to represent a multiline string).
{% endhint %}

### Calling predefined functions

#### Method 1: Direct call (no parameters)

Use `[function_name]` format to call predefined functions:

```yaml
Bottom:
  type: 'multi'
  buttons:
    test-functions:
      text: '&aTest Predefined Functions'
      actions:
        - 'js: [show_health]'
        - 'js: [greet]'
```

#### Method 2: Pass parameters

Add space-separated parameters after the function name:

```yaml
Bottom:
  type: 'multi'
  buttons:
    pass-args:
      text: '&cPass Parameters'
      actions:
        # Format: [function_name] parameter1 parameter2 parameter3 ...
        - 'js: [pass_args] %player_name% 50 {data:test} default_value'
```

### Supported parameter types

| Parameter type         | Example          | Description                  |
| ---------------------- | ---------------- | ---------------------------- |
| **String**             | `Hello`          | Text passed directly         |
| **PAPI variable**      | `%player_name%`  | PlaceholderAPI variable      |
| **Player data**        | `{data:money}`   | Data stored by the player    |
| **Global data**        | `{gdata:config}` | Data stored globally         |
| **Input box variable** | `$(input1)`      | Value of the input component |
| **Number**             | `50`, `3.14`     | Numeric type                 |

**Parameter access example:**

```javascript
// Access parameters using the args array in JavaScript
var name = args[0];     // Access the first parameter
var level = args[1];    // Access the second parameter
var data = args[2];     // Access the third parameter
var input = args[3];    // Access the fourth parameter

// Combine all parameters
var allArgs = args.join(", ");

// Check whether a parameter exists
if (args[0]) {
    player.sendMessage("First parameter: " + args[0]);
}

// Get the number of parameters
var count = args.length;
```

{% hint style="warning" %}

* Parameters are separated by spaces, and parameters cannot contain spaces
* Use `args[0]`、`args[1]` and so on to access parameters (starting from 0)
* `args` is a JavaScript array and supports all array methods
* If a parameter does not exist, accessing it will return `undefined`
* It is recommended to check whether a parameter exists before using it
  {% endhint %}

***

## 💡 Usage Examples

### Example 1: Sending messages and variables

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&aShow Information'
    actions:
      - 'js: player.sendMessage("§aYour name: §f" + name);'
      - 'js: player.sendMessage("§aYour UUID: §f" + uuid);'
      - 'js: player.sendMessage("§aCurrent world: §f" + world.getName());'
```

### Example 2: Mathematical calculation

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&bCalculation Result'
    actions:
      - 'js: var num1 = 42;'
      - 'js: var num2 = 17;'
      - 'js: var sum = num1 + num2;'
      - 'js: player.sendMessage("§b" + num1 + " + " + num2 + " = §f" + sum);'
```

### Example 3: Random number

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&eGenerate Random Number'
    actions:
      - 'js: var random = Math.floor(Math.random() * 100);'
      - 'js: player.sendMessage("§eRandom number: §f" + random);'
```

### Example 4: Conditional judgment

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&cCheck Health'
    actions:
      - 'js: var health = player.getHealth();'
      - 'js: if (health > 15) { player.sendMessage("§aYou are very healthy!"); }'
      - 'js: else if (health > 5) { player.sendMessage("§eYour health is still okay."); }'
      - 'js: else { player.sendMessage("§cYou need healing!"); }'
```

### Example 5: Accessing the Bukkit API

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&dServer Information'
    actions:
      - 'js: var onlinePlayers = Bukkit.getOnlinePlayers().size();'
      - 'js: var maxPlayers = Bukkit.getMaxPlayers();'
      - 'js: player.sendMessage("§dOnline players: §f" + onlinePlayers + "/" + maxPlayers);'
```

### Example 6: Delayed execution

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&eCountdown'
    actions:
      - 'js: for (var i = 5; i >= 1; i--) {'
      - 'js:   var delayTicks = (5 - i) * 20;'
      - 'js:   (function(num) {'
      - 'js:     delay(delayTicks, function() {'
      - 'js:       player.sendMessage("§e" + num + "...");'
      - 'js:     });'
      - 'js:   })(i);'
      - 'js: }'
      - 'js: delay(100, function() { player.sendMessage("§aStart!"); });'
```

### Example 7: Using predefined functions (no parameters)

```yaml
Title: '&6Health Check Menu'

JavaScript:
  show_health: |
    var health = player.getHealth();
    var maxHealth = player.getMaxHealth();
    player.sendMessage("§eHealth: §f" + health + "/" + maxHealth);

  greet: |
    player.sendMessage("§aHello, " + name + "!");

Bottom:
  type: 'multi'
  buttons:
    health:
      text: '&aView Health'
      actions:
        - 'js: [show_health]'
    
    greet:
      text: '&bSay Hello'
      actions:
        - 'js: [greet]'
```

### Example 8: Using predefined functions (with parameters)

```yaml
Title: '&6Friend Transfer Test'

JavaScript:
  process_data: |
    var playerName = args[0];
    var playerLevel = args[1];
    var money = args[2];
    var note = args[3];

    player.sendMessage("§aPlayer: §f" + playerName);
    player.sendMessage("§aLevel: §f" + playerLevel);
    player.sendMessage("§aCoins: §f" + money);
    player.sendMessage("§aNote: §f" + note);

Inputs:
  level:
    type: 'slider'
    text: '&aSelect Level'
    min: 1
    max: 100
    default: 10

  note_input:
    type: 'input'
    text: '&7Note information'
    default: 'None'

Bottom:
  type: 'notice'
  confirm:
    text: '&eProcess Data'
    actions:
      - 'js: [process_data] %player_name% $(level) {data:money} $(note_input)'
```

***

## 🎯 Advanced Tips

### Create custom functions

Define custom functions in events, then call them in buttons:

```yaml
Events:
  Open:
    - 'js: function getRandomItem(items) {'
    - 'js:   return items[Math.floor(Math.random() * items.length)];'
    - 'js: }'

Bottom:
  type: 'notice'
  confirm:
    text: '&6Random Loot'
    actions:
      - 'js: var item = getRandomItem(["Diamond", "Gold", "Iron", "Coal"]);'
      - 'js: player.sendMessage("You found: §e" + item);'
```

### Use delayed execution

```yaml
Bottom:
  type: 'notice'
  confirm:
    text: '&eDelayed Message'
    actions:
      - 'js: delay(20, function() { player.sendMessage("§eAfter 1 second"); });'
      - 'js: delay(40, function() { player.sendMessage("§eAfter 2 seconds"); });'
      - 'js: delay(60, function() { player.sendMessage("§eAfter 3 seconds"); });'
```

***

## 🐛 Troubleshooting

### JavaScript features unavailable?

If you see "JavaScript support disabled" or a similar error:

**Issue 1: Nashorn library not loaded**

1. On first startup, the server will automatically download the Nashorn dependency in the background
2. After the download is complete, you need to**restart the server**to load JavaScript support
3. Check the console for `JavaScript support enabled (Nashorn 15.3 engine)` message

**Issue 2: Download failed**

1. Check your network connection
2. Look for error messages in the console log
3. Make sure the server has write permissions

### JavaScript code execution error?

If JavaScript code execution fails:

1. **Check the syntax**: Make sure the JavaScript syntax is correct (pay attention to semicolons, parentheses, etc.)
2. **Check the logs**: The console will show detailed error information, including line numbers and error types
3. **Variable check**: Make sure the variables and functions used are defined
4. **Type check**: Confirm whether the variable types are correct

**Common error examples:**

```
JavaScript execution error: ReferenceError: "undefinedVar" is not defined
JavaScript execution error: SyntaxError: Unexpected token
```

***

## ✅ Best Practices

### 1. Keep the code simple

Avoid writing overly complex JavaScript code in menus. For complex logic, it is recommended to:

* Split it into multiple predefined functions
* Use clearly named function names
* Add appropriate comments

### 2. Prefer built-in features

For simple conditional checks, prefer KaMenu's `condition` feature:

```yaml
# ✅ Recommended: Use KaMenu conditions
- condition: "%player_level% >= 10"
  allow:
    - 'tell: Level is sufficient'
  deny:
    - 'tell: Level is insufficient'

# ⚠️ Possible but not recommended: Use JavaScript
- 'js: if (player.getLevel() >= 10) { player.sendMessage("Level is sufficient"); }'
```

### 3. Error handling

Use try-catch to handle possible errors:

```javascript
try {
    var result = someFunction();
    player.sendMessage("Result: " + result);
} catch (e) {
    player.sendMessage("An error occurred: " + e.message);
}
```

### 4. Performance considerations

* Avoid running time-consuming loops or calculations on every click
* Use delayed execution to spread the load
* Cache repeatedly used calculation results

### 5. Security

* Do not perform sensitive operations in JavaScript (such as database writes)
* Handle user input data carefully
* Avoid exposing internal server information

***

## 📚 Complete Example Menu

View `menus/example/javascript_demo.yml` for more complete examples!

This example menu includes:

* Basic JavaScript usage
* Random numbers and mathematical calculations
* Conditional logic
* Bukkit API access
* Predefined functions (no parameters)
* Predefined functions (with parameters)
* Delayed execution

***

## ⚠️ Notes

* JavaScript code runs on the**server side**execution
* The context for each player's click is**independent**of each other
* Complex JavaScript code may affect server performance
* It is recommended in production environments**test thoroughly**before deployment
* The Nashorn engine is based on the ECMAScript 5.1 standard and does not support ES6+ syntax

***

## 🚀 Next Steps

After mastering JavaScript features, you can:

1. **Learn more about other features**:
   * [🤖 Actions](/plugins/kamenu-en/menu/actions.md) - Learn all available actions
   * [🔍 Conditional logic](/plugins/kamenu-en/menu/conditions.md) - Master the use of conditional logic
   * [💾 Data storage](/plugins/kamenu-en/data/storage.md) - Use databases to store data
2. **Explore advanced applications**:
   * Create dynamic menus with data storage
   * Use JavaScript to implement complex business logic
   * Create reusable JavaScript function libraries
3. **See more examples**:
   * Browse `menus/example/` example menus in the directory
   * Learn menu configurations shared by the community

***

## 💬 Need help?

If you encounter problems or have suggestions:

* View `menus/example/javascript_demo.yml` Example file
* View detailed error logs in the console
* Submit an Issue or Discussion on GitHub
* Join the community Discord for help


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://katacr.gitbook.io/plugins/kamenu-en/menu/javascript.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
