TX Text Control .NET Server

Editing Text

Topic

Inserting text blocks, replacing placeholders and navigating through a document all come down to reading and changing the text in the Document Editor. Through the JavaScript API, your code selects text, sets the input position, inserts and replaces text, reads paragraphs and lines and finds text, and it works with undo and redo, the clipboard and the overwrite mode.

The examples extend the view of the ASP.NET Core tutorial, whose document contains the text "Welcome to Text Control". Formatting is explained on Text and Paragraph Formatting.

Text Positions

Text positions are counted in characters. Two counting methods occur in the API, and the reference states which one a member uses:

  • Zero-based positions start with 0 for the first character. The selection, find and setInputPositionByTextPosition use them.
  • One-based numbers start with 1. The getStart methods of paragraphs, lines and pages return them.

A paragraph end or a line break counts as one character.

Select Text

Your code selects text in three ways:

  • Assign the start position and the length to TXTextControl.selection: TXTextControl.selection = { start: 0, length: 7 };.
  • Call select with the start, the length and a callback. Use it when the next step depends on the selection, because the callback tells you when it has been made.
  • Call selectAll to select the whole text, or selectWord to select the word at the input position.

The Selection object passes the selected text with getText, and its position with getStart and getLength.

Insert and Replace Text

Selection.setText replaces the selected text. Without a selection, it inserts the text at the input position; with an empty string, it deletes the selection. Selection.load inserts formatted text instead, for example HTML encoded with btoaUTF8.

To place the input position first, setInputPositionByTextPosition takes a zero-based text position and a value of TextFieldPosition, which decides whether the position lies inside or outside a field at that place. setInputPositionByPage takes a page, a line and a column instead.

The following code inserts text at the beginning of the document, inserts formatted text at the input position, inserts a page break and logs the paragraph at the input position. Add it below the editor in Views/Home/Index.cshtml:

<input type="button" onclick="insertAtStart()" value="Insert at Start" />
<input type="button" onclick="insertFormattedText()" value="Insert Formatted Text" />
<input type="button" onclick="insertPageBreak()" value="Insert Page Break" />
<input type="button" onclick="logCurrentParagraph()" value="Log Paragraph" />

<script>
    function insertAtStart() {
        // The first text position is 0
        TXTextControl.setInputPositionByTextPosition(0,
            TXTextControl.TextFieldPosition.OutsideTextField, function () {
                TXTextControl.selection.setText("DRAFT ", function () {
                    TXTextControl.focus();
                });
            });
    }

    function insertFormattedText() {
        var html = "<p>Signed by <strong>Tim Typer</strong></p>";
        TXTextControl.selection.load(TXTextControl.StreamType.HTMLFormat, TXTextControl.btoaUTF8(html));
    }

    function insertPageBreak() {
        TXTextControl.inputPosition.getTextPosition(function (textPosition) {
            TXTextControl.textChars.addControlChar(TXTextControl.ControlChars.PageBreak, textPosition,
                function (added) {
                    console.log("Page break inserted: " + added);
                });
        });
    }

    function logCurrentParagraph() {
        TXTextControl.inputPosition.getTextPosition(function (textPosition) {
            TXTextControl.paragraphs.getItem(textPosition, function (paragraph) {
                paragraph.getText(function (text) {
                    console.log("Paragraph at " + textPosition + ": " + text);
                });
            });
        });
    }
</script>

The code works as follows:

Read the Text of a Document

The main text and every other text part provide collections of their building blocks:

CollectionContainsUseful methods
paragraphsThe paragraphsParagraph.getText, getStart, getLength, select
linesThe linesLine.getText, getPage
textCharsThe charactersTextChar.getChar; the collection also adds and removes characters
TXTextControl.pagesThe pages, in the page viewPage.getStart, getLength, select

TXTextControl.getText passes the complete text of the document, and getPageCount the number of pages. Note the parameter orders in the reference: LineCollection.getItem, for example, takes the callback before the text position.

Remove Empty Pages

A template that contains a page break followed by a paragraph that starts on a new page, or two page breaks in a row, produces an empty page. Such a page contains only the break character, so its length is 1. The following function goes through the pages from the last to the first and deletes that character on every empty page:

function removeEmptyPages() {
  TXTextControl.pages.getCount(function (count) {

    function processPage(index) {
      if (index < 0) {
        return; // all pages processed
      }

      TXTextControl.pages.elementAt(index, function (page) {
        page.getLength(function (length) {
          if (length !== 1) {
            processPage(index - 1);
            return;
          }

          // getStart is one-based, select is zero-based
          page.getStart(function (start) {
            TXTextControl.select(start - 1, length, function () {
              TXTextControl.selection.setText("", function () {
                processPage(index - 1);
              });
            });
          });
        });
      });
    }

    // Start with the last page, so that deleting doesn't renumber the pages still to check
    processPage(count - 1);
  });
}

The code works as follows:

  • TXTextControl.pages returns the PageCollection, which exists only in the page view. getCount passes the number of pages and elementAt a page by its zero-based index.
  • Page.getLength passes the number of characters on the page. A length of 1 means that the page holds nothing but a page or section break.
  • Page.getStart passes the one-based position of the first character, so the code subtracts 1 for select. setText with an empty string deletes the selected break.
  • processPage calls itself from the callbacks, so each page is checked only after the previous change has been made.

In a letter with two page breaks in a row, the function reduces three pages to two. The blog article Removing Empty Pages in TX Text Control with JavaScript shows the scenario. On the server, "Remove Empty Pages" in Pages does the same with ServerTextControl.

Find and Replace Text

find searches for a text from a zero-based start position (-1 for the input position) and passes the position of the first match to its callback, or -1. Its optional third parameter is a value of FindOptions: MatchCase, MatchWholeWord, Reverse, NoHighlight or NoMessageBox.

The following code replaces every "Welcome" with "Hello" as one undo step and undoes the last action. A helper function turns a method with a callback into a promise, as described on JavaScript API:

<input type="button" onclick="replaceAll('Welcome', 'Hello')" value="Replace All" />
<input type="button" onclick="undoLastAction()" value="Undo" />

<script>
    // Calls a method of the JavaScript API and waits for its callback
    function request(target, method) {
        var args = Array.prototype.slice.call(arguments, 2);
        return new Promise(function (resolve, reject) {
            target[method].apply(target, args.concat([resolve, reject]));
        });
    }

    async function replaceAll(searchText, replacementText) {
        var count = 0;
        var options = TXTextControl.FindOptions.NoMessageBox;

        // Undo all replacements in one step
        await request(TXTextControl, "beginUndoAction", "Replace all");

        var position = await request(TXTextControl, "find", searchText, 0, options);

        while (position !== -1) {
            await request(TXTextControl, "select", position, searchText.length);
            await request(TXTextControl.selection, "setText", replacementText);
            count++;

            // Continue behind the inserted text
            position = await request(TXTextControl, "find", searchText,
                position + replacementText.length, options);
        }

        await request(TXTextControl, "endUndoAction");
        console.log(count + " replacements");
    }

    async function undoLastAction() {
        var canUndo = await request(TXTextControl, "getCanUndo");

        if (!canUndo) {
            console.log("Nothing to undo");
            return;
        }

        var actionName = await request(TXTextControl, "getUndoActionName");
        await request(TXTextControl, "undo");
        console.log("Undone: " + actionName);
    }
</script>
  • NoMessageBox keeps find from showing message boxes about the search result.
  • select selects each match, and setText replaces it. The next search starts behind the inserted text, so that a replacement that contains the search text isn't found again.
  • beginUndoAction and endUndoAction make all replacements one step, named "Replace all".

Click Replace All: "Welcome" becomes "Hello". Click Undo to restore it in one step. For users, showDialog opens the built-in dialog boxes with FindDialog or ReplaceDialog, which the Find... and Replace... buttons of the Home tab open as well:

TXTextControl.showDialog(TXTextControl.DialogBoxKind.ReplaceDialog);

Replace dialog box of the Document Editor with the Find what and Replace with boxes, the options Match case, Match whole word only and Search up, and check boxes for searching the main text, footnotes, text frames and headers and footers

Undo and Redo

MethodDoes
undo, redoUndoes or redoes the last action
getCanUndo, getCanRedoPasses whether there is something to undo or redo
getUndoActionName, getRedoActionNamePasses the name of that action; getUndoActionName passes null if nothing can be undone
clearUndoClears the undo buffer
setAllowUndoTurns the undo buffer on or off

Without grouping, each change is a step of its own: inserting a table and filling its 25 cells, for example, makes 26 steps. Grouped changes are also displayed at once, which avoids flickering, as the blog article JavaScript: Avoid Flickering and Visual Updates by Grouping Undo Steps shows.

Clipboard, Overwrite Mode and Drag and Drop

  • Clipboard. Users copy, cut and paste with Ctrl+C, Ctrl+X and Ctrl+V. clipboardMode decides which clipboard these keys use: Client from ClipboardMode uses the clipboard of the user's computer, which accepts RTF, HTML, plain text and images from other applications; Server uses an internal clipboard that keeps all TX Text Control features. The JavaScript API has no copy or paste methods. getCanCopy and getCanPaste pass whether copying and pasting are possible, and the textPasted event lets your code cancel pasted text.
  • Overwrite mode. setInsertionMode switches between Insert and Overwrite from InsertionMode, and getInsertionMode passes the current mode. Added in version 34.0.
  • Deleting. clear deletes the selected text or the character to the right of the input position. resetContents deletes the whole content.
  • Drag and drop. setAllowDrag and setAllowDrop set whether the editor can be the source of a drag operation and whether it accepts dropped data. The textDropped and fileDropped events let your code cancel a drop.

The following lines use the client clipboard and log pasted text:

TXTextControl.addEventListener("textControlLoaded", function () {
  // Ctrl+C, Ctrl+X and Ctrl+V use the clipboard of the local computer
  TXTextControl.clipboardMode = TXTextControl.ClipboardMode.Client;

  TXTextControl.addEventListener("textPasted", function (e) {
    console.log("Pasted " + e.textType + ": " + e.text);
  });
});

Whether text can be changed at all depends on the edit mode and the document permissions; see Document Protection. The events for the input position, the clipboard, pasting and dropping are listed on Events.

Keyboard and Mouse

Users edit in the Document Editor with the keys and mouse actions of a word processor. The following tables list the assignments of TX Text Control that apply to the Document Editor of TXTextControl.Web 34.5.0.

KeyAction
Left Arrow, Right ArrowMoves the input position one character to the left or to the right
Up Arrow, Down ArrowMoves the input position one line up or down
Home, EndMoves the input position to the beginning or the end of the line
Ctrl+Left Arrow, Ctrl+Right ArrowMoves the input position to the beginning of the current or the next word
Ctrl+Home, Ctrl+EndMoves the input position to the beginning or the end of the text
Page Up, Page DownScrolls the text up or down by the height of the visible area minus one line, if the editor has a vertical scroll bar
Shift+Page Up, Shift+Page DownScrolls the document horizontally
Shift together with an arrow key, Home, End or Ctrl+arrow keyExtends the selection to the new input position
Shift+EnterInserts a line break without starting a new paragraph
Ctrl+EnterInserts a page break
Ctrl+Shift+SpaceInserts a non-breaking space
DelDeletes the selected text
Ctrl+Z, Ctrl+YUndoes the last action, or restores the last undone action

Hyphens and dashes have keys of their own. The second column says whether a line can be divided after the character:

KeyInsertsLine can break after it
HyphenA hyphen or minus signYes
Ctrl+Shift+HyphenA non-breaking hyphen for compound wordsNo
Ctrl+Minus on the numeric keypadAn en dashYes
Ctrl+Alt+Minus on the numeric keypadAn em dashYes
Mouse actionAction
ClickMoves the input position to the point of the click, or selects the image or text frame that is clicked
Click and dragSelects the text from the point where the button is pressed to the point where it is released, or moves an image or a text frame
Shift+clickExtends the selection to the position of the click
Double-clickSelects the word
Double-click and dragExtends the selection word by word
Triple-clickSelects the paragraph
Triple-click and dragExtends the selection paragraph by paragraph
Drag selected text and drop itMoves the text to the drop position
Ctrl while dragging selected text and dropping itCopies the text to the drop position
Click in the left page marginSelects the line next to the click

In tables, these keys and mouse actions apply as well:

Key or mouse actionAction
Tab, Shift+TabSelects the next or the previous cell
Click left of a table rowSelects the table row
Click the top of a table column in the first rowSelects the table column

Dragging and dropping text works only while setAllowDrag and setAllowDrop allow it, as described in Clipboard, Overwrite Mode and Drag and Drop.

The desktop controls of TX Text Control assign further keys: Ctrl+Backspace and Shift+Backspace to delete the previous word, Ctrl+Hyphen to insert an invisible hyphen at which a word can be divided at the end of a line, Ctrl+Shift+Enter to insert a column break, Ctrl+Page Up and Ctrl+Page Down to zoom, Ctrl+Tab to insert a tab character in a table cell, and a double-click in a table cell to select the cell. These assignments aren't confirmed for the Document Editor in a browser and are left out of the tables. How your code and the ribbon set the zoom is described in Viewing and Printing.

The keys for the clipboard are described in Clipboard, Overwrite Mode and Drag and Drop.

Next Steps