> For the complete documentation index, see [llms.txt](https://docs.eximee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/formatery/api-formatera.md).

# Formatter API

{% hint style="danger" %}
Note!

Do not add comments in formatters in Eximee Designer - they may cause errors in the application!
{% endhint %}

## Implementation of a new formatter <a href="#tworzenieformatera-implementacjanowegoformatera" id="tworzenieformatera-implementacjanowegoformatera"></a>

After creating a new formatter in the workspace, a text editor with the formatter implementation template will open.

The formatter is represented as a JavaScript function with the signature initially specified in the template:

```
function formatText(input) {
         return { 
                formattedText: input.textToFormat,
                cursorPosition: input.cursorPosition 
          };
}
```

Description of the formatter function signature:

<table><thead><tr><th width="214.9166259765625">Parameter name</th><th width="150.3499755859375">Type</th><th width="103.300048828125">Type</th><th>Description</th></tr></thead><tbody><tr><td><strong>input.textToFormat</strong></td><td><strong>input</strong></td><td><strong>String</strong><br><strong>(JAVA)</strong></td><td><strong>Text to be formatted.</strong><br>The JAVA type of the variable should be converted to <strong>JavaScript</strong> (new String(input.textToFormat); ) in order to call methods for the type <strong>String</strong> (e.g. replace())</td></tr><tr><td><strong>input.locale</strong></td><td><strong>input</strong></td><td><strong>String</strong><br><strong>(JAVA)</strong></td><td><strong>A string specifying the language in which the application is displayed (e.g. "pl", "en").</strong><br>The JAVA type of the variable should be converted to <strong>JavaScript</strong> (new String(input.locale); ) in order to call methods for the type <strong>String</strong> (e.g. replace())</td></tr><tr><td><strong>input.cursorPosition</strong></td><td><strong>input</strong></td><td><strong>int</strong></td><td><strong>Cursor position in the text field before formatting</strong></td></tr><tr><td><strong>output.formattedText</strong></td><td><strong>output</strong></td><td><strong>String</strong></td><td><strong>Text after formatting</strong></td></tr><tr><td><strong>output.cursorPosition</strong></td><td><strong>output</strong></td><td><strong>String</strong></td><td><strong>Cursor position in the text field after formatting</strong> - an integer in the range &#x3C;0,output.formattedText.length> - if a number outside the range is provided, the webforms application will not change the cursor position (the browser's default behavior will be preserved)</td></tr></tbody></table>

The formatter implementation should be done by changing only the contents of the function.

For proper operation, the formatter must be a valid function of the **JavaScript**. Otherwise, when attempting to save the formatter in the current user space, an appropriate system error message will be generated.

<details>

<summary>Sample formatter code (adds a thousands separator to the number and always places the cursor at the end):</summary>

```
function formatText(input) {
    if (typeof input.textToFormat == 'undefined') {
        return  {
            formattedText: "",
            cursorPosition: -1
        };
    }
     
    let number_format = function(number, decimals, dec_point, thousands_sep) {
        number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
        let n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0
                    : Math.abs(decimals), sep = (typeof thousands_sep === 'undefined') ? ','
                    : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.'
                    : dec_point, s = '', toFixedFix = function(n, prec) {
            let k = Math.pow(10, prec);
            return '' + Math.round(n * k) / k;
        };
        s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
        if (s[0].length > 3) {
            s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
        }
        return s.join(dec);
    }
     
    let textToFormat = new String(input.textToFormat + "");
    textToFormat = textToFormat.replace(/\s+/g, '');
    textToFormat = textToFormat.replace(/,/g, '.')
    textToFormat = textToFormat.replace(/[^\d^\.]/g,'');
     
    let temp = parseFloat(textToFormat);
    if(isNaN(temp)){
        return {
            formattedText: "",
            cursorPosition: -1
        };
    }
    let resultText = number_format(temp, 2, ',', ' ');
     
    return {
        formattedText: resultText,
        cursorPosition: resultText.length
    };
}
```

</details>

Version with the caret set to the natural position (taking thousands separators into account). The formatter below will not work for a TextField of type EMAIL due to JavaScript limitations (input type="email" does not implement retrieving the caret position).

<details>

<summary>Version with the caret set to the natural position</summary>

```
function formatText(input) {
 
    let spacesInInput;
    let localCursorPosition;
     
    if(input.cursorPosition) {
        localCursorPosition = input.cursorPosition;
    }
     
    if(input.textToFormat) {
        if(input.textToFormat.startsWith('0')) {
            localCursorPosition--;
        }
        spacesInInput = (input.textToFormat.match(/ /g) || []).length;
    } else {
        return  {
            formattedText: "",
            cursorPosition: -1
        };
    }
     
    let number_format = function(number, decimals, dec_point, thousands_sep) {
        number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
        let n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0
                    : Math.abs(decimals), sep = (typeof thousands_sep === 'undefined') ? ','
                    : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.'
                    : dec_point, s = '', toFixedFix = function(n, prec) {
            let k = Math.pow(10, prec);
            return '' + Math.round(n * k) / k;
        };
        s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
        if (s[0].length > 3) {
            s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
        }
        return s.join(dec);
    }
     
    let textToFormat = new String(input.textToFormat + "");
    textToFormat = textToFormat.replace(/\s+/g, '');
    textToFormat = textToFormat.replace(/,/g, '.')
    textToFormat = textToFormat.replace(/[^\d^\.]/g,'');
     
    let temp = parseFloat(textToFormat);
    if(isNaN(temp)){
        return {
            formattedText: "",
            cursorPosition: -1
        };
    }
    let resultText = number_format(temp, 2, ',', ' ');
     
    let caretPosition = localCursorPosition ? localCursorPosition : resultText.length;
    let spacesAfterParse = (resultText.match(/ /g) || []).length;
 
    if(spacesAfterParse > spacesInInput) {
        caretPosition++;
    } else if(spacesAfterParse < spacesInInput) {
        caretPosition--;
    }
 
    return {
        formattedText: resultText,
        cursorPosition: caretPosition
    };
}
```

</details>

## Formatter handling according to the previous template

For formatters created according to the previous template shown below, the cursor after formatting the text will be set to the end if it was set to the end before formatting. If the cursor was set within the text content, then after formatting the text it will remain in the same place where it was set before formatting.

```
function formatText(textToFormat, locale) {
        return  formattedText;
}
```

Description of the previous formatter function signature:

| Parameter name    | Type       | Type       | Description                                                                                  |
| ----------------- | ---------- | ---------- | -------------------------------------------------------------------------------------------- |
| **textToFormat**  | **input**  | **String** | **Text to be formatted**                                                                     |
| **locale**        | **input**  | **String** | **A string specifying the language in which the application is displayed (e.g. "pl", "en")** |
| **formattedText** | **output** | **String** | **Text after formatting**                                                                    |

## Limitations <a href="#tworzenieformatera-ograniczenia" id="tworzenieformatera-ograniczenia"></a>

* Formatters cannot use prototype inheritance mechanisms
* In formatters that format outputs from an object service, objects passed to the formatter are Java objects; to perform string operations, they must be converted to JavaScript objects (e.g.: const textToFormat = new String(input.textToFormat + ""))

{% hint style="info" %}
**Demo request:**\
demoFormatery
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/formatery/api-formatera.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
