# JavaScript

> Load the DS Server Document Editor into a plain HTML page with TXTextControl.init, load and save documents, and request the access token in ASP.NET Core.

- **Product:** DS Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** DS Server > Getting Started > Document Editor > JavaScript
- **4597 words**
- **Web URL:** https://docs.textcontrol.com/ds-server/getting-started/document-editor/javascript/
- **LLMs URL:** https://docs.textcontrol.com/ds-server/getting-started/document-editor/javascript/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/ds-server/getting-started/document-editor/javascript/llms-full.txt

---

Without a framework or an npm package, a plain HTML page shows the Document Editor with one script that DS Server serves itself. The page calls `TXTextControl.init()` with the address of DS Server and the credentials of a security profile, and then works with the JavaScript API. At the end, a small ASP.NET Core endpoint requests the access token on the server, so the client secret no longer appears in the page.

### Prerequisites

- A text editor, a web browser and a local web server to serve the page, such as the one your IDE provides.
- A DS Server that is installed, licensed and reachable from the browser, as the [Installation Guide](https://docs.textcontrol.com/ds-server/installation/llms-full.txt) and [Licensing](https://docs.textcontrol.com/ds-server/administration/licensing/llms-full.txt) describe. The samples use `http://localhost:5000`.
- For [Keep the Client Secret on Your Server](#keep-the-client-secret-on-your-server): Visual Studio 2026 or the .NET 10 SDK.

With a trial token instead of your own server, skip the next section, use `https://trial.dsserver.io` as the service URL and the client ID and secret of the token. [Trial Token](https://docs.textcontrol.com/ds-server/getting-started/trial-token/llms-full.txt) explains how to get one.

### How the Page Works

Everything the page needs comes from DS Server:

- The script *ds-server-document-editor.js* is part of every DS Server installation, in its *wwwroot/documenteditor/JS* folder, and DS Server serves it at `<service URL>/documenteditor/JS/ds-server-document-editor.js` without authentication. It defines the global [`TXTextControl`](https://docs.textcontrol.com/ds-server/reference/javascript/txtextcontrol/llms-full.txt) object.
- `TXTextControl.init()` creates the editor in a `div` element. With a client ID and secret, the script requests an access token from `<service URL>/oauth/token` with the client credentials grant type. With an access token, it uses that token.
- The editor then opens a WebSocket connection to `<service URL>/documenteditor/WS`, with `wss:` for an `https` service URL and `ws:` for `http`. Every change in the browser is synchronized over this connection.

### Prepare a Security Profile

1. In the admin portal of DS Server, choose **Security Profiles**, click **+ Create new profile**, enter a name and click **Create**.
2. Copy the **Client ID** and the **Client Secret** of the new profile.
3. Switch on **Allow "Client Credentials" grant type** and click **Done**. The grant type is off by default. The page needs it to request a token with the client ID and secret.

    ![The Authorized Redirect URIs list of a security profile and, below it, the Allow Client Credentials grant type switch turned on in green.](https://docs.textcontrol.com/assets/img/ds-server/portal-client-credentials.221d7483.webp)

A page that contains the client secret shows it to every visitor. Use it for a first test only; the [last section](#keep-the-client-secret-on-your-server) removes it. [OAuth Grant Types](https://docs.textcontrol.com/ds-server/administration/oauth-grant-types/llms-full.txt) describes the setting.

### Create the HTML Page

Create a file named *index.html* with the following content. Replace `http://localhost:5000` in both places with the address of your DS Server, and the client ID and secret with the values of your security profile.

```html

<html>
<head>
  <meta charset="utf-8" />
  <title>DS Server Document Editor</title>
  <script src="http://localhost:5000/documenteditor/JS/ds-server-document-editor.js"></script>
  <style>
    #txDocumentEditorContainer {
      width: 1000px;
      height: 600px;
      display: inline-block;
      position: relative;
    }
  </style>
</head>
<body>

  <div id="txDocumentEditorContainer"></div>

  <script>
    window.addEventListener("load", function () {
      TXTextControl.init({
        containerID: "txDocumentEditorContainer",
        serviceURL: "http://localhost:5000",
        authSettings: {
          clientId: "<client ID>",
          clientSecret: "<client secret>"
        }
      });

      TXTextControl.addEventListener("textControlLoaded", function () {
        TXTextControl.selection.setText("Welcome to DS Server");
      });
    });
  </script>

</body>
</html>
```

#### What the Page Does

- The `script` element in the `head` loads the editor script from DS Server.
- The `div` with the ID `txDocumentEditorContainer` is the container of the editor. The CSS gives it an explicit width and height.
- `TXTextControl.init()` creates the editor. `containerID` is the ID of the container and `serviceURL` the address of DS Server; both are required. `authSettings` holds either `clientId` and `clientSecret` or an `accessToken`. If it holds both, the script warns in the browser console and uses only the token.
- [`TXTextControl.addEventListener()`](https://docs.textcontrol.com/ds-server/reference/javascript/txtextcontrol/addeventlistener-method/llms-full.txt) registers a handler for the `textControlLoaded` event, which fires when the editor is ready. [`selection.setText()`](https://docs.textcontrol.com/ds-server/reference/javascript/selection/settext-method/llms-full.txt) inserts the text at the input position.

### Open the Page

Serve *index.html* from your local web server and open it in the browser. The page shows the Document Editor with its ribbon bar and the text "Welcome to DS Server".

The ribbon of the DS Server editor has no **File** tab, because the script always hides it. Documents are loaded and saved with the JavaScript API.

### Load and Save Documents

Add two buttons below the container `div`:

```html
<input type="button" onclick="loadHtml()" value="Load Document" />
<input type="button" onclick="saveAsHtml()" value="Save as HTML" />
```

Then add the two functions to the existing `script` element, outside the `load` handler:

```javascript
function loadHtml() {
  TXTextControl.loadDocument(TXTextControl.StreamType.HTMLFormat,
    btoa("<p>Loaded with <strong>JavaScript</strong></p>"));
}

function saveAsHtml() {
  TXTextControl.saveDocument(TXTextControl.StreamType.HTMLFormat, function (e) {
    console.log(e.data);
  });
}
```

- [`loadDocument()`](https://docs.textcontrol.com/ds-server/reference/javascript/txtextcontrol/loaddocument-method/llms-full.txt) replaces the content of the editor. The first parameter is a value of the [`StreamType`](https://docs.textcontrol.com/ds-server/reference/javascript/txtextcontrol/streamtype-enumeration/llms-full.txt) enumeration, the second the document as a Base64-encoded string. For text with characters outside the Latin-1 range, encode it with [`TXTextControl.btoaUTF8()`](https://docs.textcontrol.com/ds-server/reference/javascript/txtextcontrol/btoautf8-method/llms-full.txt) instead of `btoa`.
- [`saveDocument()`](https://docs.textcontrol.com/ds-server/reference/javascript/txtextcontrol/savedocument-method/llms-full.txt) passes the document in the requested format to the callback. Here, the HTML is written to the browser console.

### Editor Settings

`TXTextControl.init()` also takes an `editorSettings` object, the [`DocumentEditorSettings`](https://docs.textcontrol.com/ds-server/reference/javascript/documenteditorsettings/llms-full.txt) of the JavaScript API:

| Setting | Description |
|---|---|
| `editMode` | Whether the text can be edited, a number: `1` Edit, `2` ReadAndSelect, `3` ReadOnly, `2048` UsePassword |
| `userNames` | An array of the names of users who may edit the editable regions assigned to them |
| `culture` | The culture that formats values such as dates, for example `"de-DE"` |
| `uiCulture` | The culture of the user interface strings, for example `"en-US"` |
| `contextMenusEnabled` | Whether a right-click opens a context menu |
| `formattingPrinter` | The name of a printer whose text dimensions are used to format the document |
| `reconnectTimeout` | The time in seconds after which the editor stops trying to reconnect after a connection loss |
| `customQueryParams` | An object of query parameters that the script adds to the address of the WebSocket connection |
| `connectionID` | The unique ID of the connection, generated by the script when it is not set |
| `webSocketProtocols` | An array of additional WebSocket subprotocols passed to the WebSocket connection, for example to pass authentication tokens |

For example, the following call opens the document read-only for all users except the editable regions of *user1*:

```javascript
TXTextControl.init({
  containerID: "txDocumentEditorContainer",
  serviceURL: "http://localhost:5000",
  authSettings: {
    clientId: "<client ID>",
    clientSecret: "<client secret>"
  },
  editorSettings: {
    editMode: 3,
    userNames: ["user1"]
  }
});
```

### Keep the Client Secret on Your Server

In production, the page gets an access token from your own server and passes it to `TXTextControl.init()` in `authSettings.accessToken`. The following ASP.NET Core application requests the token from DS Server and serves the HTML page itself, so no cross-origin setup is needed.

1. Create an empty ASP.NET Core project and change to its folder:

    ```bash
    dotnet new web -n MyTokenApp -f net10.0
    cd MyTokenApp
    ```
2. Add the address of DS Server and the credentials of the security profile to *appsettings.json*:

    ```json
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*",
      "DSServer": {
        "ServiceUrl": "http://localhost:5000",
        "ClientId": "<client ID>",
        "ClientSecret": "<client secret>"
      }
    }
    ```
3. Replace the content of *Program.cs* with the following code:

```csharp
using System.Net.Http.Headers;
using System.Text;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient();

var app = builder.Build();

app.UseDefaultFiles();
app.UseStaticFiles();

// Returns an access token of the security profile to the page
app.MapGet("/api/token", async (IHttpClientFactory httpClientFactory, IConfiguration config) =>
{
    string serviceUrl = config["DSServer:ServiceUrl"]!;
    string credentials = $"{config["DSServer:ClientId"]}:{config["DSServer:ClientSecret"]}";

    var request = new HttpRequestMessage(HttpMethod.Post, $"{serviceUrl}/oauth/token")
    {
        Content = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["grant_type"] = "client_credentials"
        })
    };

    // DS Server expects the client ID and secret in a Basic authorization header
    request.Headers.Authorization = new AuthenticationHeaderValue(
        "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials)));

    var response = await httpClientFactory.CreateClient().SendAsync(request);
    string json = await response.Content.ReadAsStringAsync();

    return Results.Content(json, "application/json", statusCode: (int)response.StatusCode);
});

app.Run();
```

1. Create a folder named *wwwroot* in the project folder and save *index.html* in it with this `script` element at the end of the `body`, instead of the one above:

    <pre><script>
  fetch("/api/token")
    .then(response => response.json())
    .then(token => {
      TXTextControl.init({
        containerID: "txDocumentEditorContainer",
        serviceURL: "http://localhost:5000",
        authSettings: { accessToken: token.access_token }
      });

      TXTextControl.addEventListener("textControlLoaded", function () {
        TXTextControl.selection.setText("Welcome to DS Server");
      });
    });
</script></pre>

2. Run the application with <code>dotnet run</code> and open the address it prints.

The code works as follows:

- `/api/token` sends `grant_type=client_credentials` to the `/oauth/token` endpoint of DS Server. DS Server expects the client ID and the client secret in an HTTP Basic authorization header, not as form fields.
- DS Server answers with a JSON object that contains `access_token`, `token_type` (`Bearer`) and `expires_in` (`"3600"`, the lifetime in seconds as a string). The endpoint passes it to the page unchanged.
- `UseDefaultFiles()` and `UseStaticFiles()` serve *wwwroot/index.html* at the root address of the application, so the page and the endpoint share one origin.
- The page passes only the token to `TXTextControl.init()`. The client secret stays in *appsettings.json* on the server.

The endpoint gives a token to anyone who calls it, so protect it the way you protect the rest of your application, for example with its sign-in. The security profile still needs **Allow "Client Credentials" grant type**, because the server uses this grant type. [POST /oauth/token](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/token/post/llms-full.txt) in the Web API reference lists the parameters and answers of the token endpoint.

### Troubleshooting

- **"DS Server is not registered."** The editor area stays gray and shows this message when the DS Server has no license. The browser console shows `License error: DS Server is not registered.` Register the server in the admin portal, as [Licensing](https://docs.textcontrol.com/ds-server/administration/licensing/llms-full.txt) describes.

    ![The Document Editor area on an unregistered DS Server: a gray rectangle with the message box "DS Server is not registered." in its center.](https://docs.textcontrol.com/assets/img/ds-server/editor-not-registered.fc18aa7f.webp)
- **"Can't connect to the server. Please refresh the website to re-connect."** The WebSocket connection to `<service URL>/documenteditor/WS` failed. When OAuth is enabled and `init()` has no `authSettings`, DS Server refuses the connection, and the browser console shows `HTTP Authentication failed; no valid credentials available`.
- **`TXTextControl is not defined`** in the browser console: the page couldn't load the editor script. Check the address in the `script` element.
- **`Could not retrieve OAuth token. Error: unsupported_grant_type`** in the browser console: the security profile doesn't allow the client credentials grant type.

[Document Editor and Viewer](https://docs.textcontrol.com/ds-server/troubleshooting/editor-viewer/llms-full.txt) in Troubleshooting covers more problems.

### Next Steps

- [Document Editor JavaScript API](https://docs.textcontrol.com/ds-server/reference/javascript/llms-full.txt): the `TXTextControl` object and its methods and events.
- [JavaScript](https://docs.textcontrol.com/ds-server/getting-started/document-viewer/javascript/llms-full.txt): add the Document Viewer to a plain HTML page.
- [Overview](https://docs.textcontrol.com/ds-server/client-libraries/overview/llms-full.txt): compare the plain page with the client libraries.
- [Enable OAuth](https://docs.textcontrol.com/ds-server/administration/oauth/llms-full.txt): require access tokens for every request.

---

## Nearby

- **Previous:** [React](https://docs.textcontrol.com/ds-server/getting-started/document-editor/react/llms-full.txt)
- **Next:** [Document Viewer](https://docs.textcontrol.com/ds-server/getting-started/document-viewer/llms-full.txt)
