# Formatter API

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

In formatters in Eximee Designer, do not add comments - 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 provided 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>You must convert the JAVA type of the variable to the type <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>You must convert the JAVA type of the variable to the type <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>

Formatter implementation should be done by changing only the body of the function.

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

<details>

<summary>Example formatter code (adds a thousands separator to a 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 positioned at the natural position (taking thousand 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 positioned at 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>

## Handling formatters according to the previous template

For formatters created according to the previous template shown below, the cursor after formatting the text will be placed at the end if it was at the end before formatting. If the cursor was in the body of the text, then the cursor in the field after formatting will be placed in the same position as it was 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 the object service, the objects passed to the formatter are Java objects; to perform operations on strings, they must be converted to JavaScript objects (e.g.: const textToFormat = new String(input.textToFormat + ""))

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


---

# 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://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/formatery/api-formatera.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.
