Create operations

This is a guide on creating and using operations. Operations are interactions with the state of the editor instance.

For an explanation of the concept of operations, see the Operations concept page.  For a complete list of available operations, refer to the Operation API documentation.

Operations are used to enable the users of the application to do more complex things than writing simple plain text. They can be used to insert complete XML structures, alter the structure of tables, open modals, manipulate the selection, and a lot more. This guide will describe how and which operations to use for some of the most common use cases when configuring an application.

Operations for different use cases

The FontoXML platform provides a wide range of operations, both complete ones only requiring some configuration and operations specifically meant to be a step of another operation. For a complete list of operations, refer to the Operations API documentation.

Toggling inline elements

Inline elements can be divided in two groups. The first group contain elements which only apply some kind of formatting without suggesting any meaning. An example is the b element found in DITA. These elements should be configured with the configureAsInlineFormatting family. For this family, the toggle-inline-formatting-element operation should be used. The second group contains elements which describe the semantics of a text phrase. An example is the apiname element found in DITA. These elements should be configured with the configureAsInlineFrame family. For this family type, the toggle-inline-frame-element operation should be used.

The following example shows the usage of the toggle-inline-formatting-element operation for toggling a DITA b element:

{
    "b-toggle": {
        "label": "Bold",
        "description": "Applies bold formatting to text.",
        "icon": "bold",
        "keyBinding": "ctrl+b",
        "osxKeyBinding": "cmd+b",
        "steps": [
            {
                "type": "operation/toggle-inline-formatting-element",
                "data": {
                    "nodeName": "b"
                }
            }
        ]
    }
}

The following example shows the usage of the toggle-inline-frame-element operation for toggling a DITA apiname element:

{
    "apiname-insert": {
        "label": "API name",
        "description": "Provides the name of an application programming interface.",
        "steps": [
            {
                "type": "operation/toggle-inline-frame-element",
                "data": {
                    "nodeName": "apiname"
                }
            }
        ]
    }
}

As shown in the examples, both operations require the nodeName to be set.

Inserting structures

It is often necessary to not only insert just one element, but a whole XML structure. For this, the horizontal-insert and vertical-insert operations can be used. These operations differ in the way they look for the right spot for the structure to be inserted. In short, the horizontal-insert inserts the given structure somewhere under a given node, taking into account the schema. The vertical-insert takes a different approach, it tries to insert the given structure as close to the selection as possible. See the API pages of both operations for more information.

The following example shows how to insert a DITA consequence element using the horizontal-insert operation:

{
    "consequence-insert": {
        "label": "Add consequence",
        "description": "Specifies the consequence of failing to avoid a hazard.",
        "steps": [
            {
                "type": "operation/horizontal-insert",
                "data": {
                    "childNodeStructure": [
                        "consequence",
                        [
                            {
                                "bindTo": "selection",
                                "empty": true
                            }
                        ]
                    ]
                }
            }
        ]
    }
}

The example for inserting an image doubles as an example for the vertical-insert operation.

Inserting images

Operations for inserting images generally consist of two operations. The first operation, or step, opens the browse modal in which the user can select an image to insert. The second operation inserts an element representing the image and the required surrounding elements.

Note that in this example, data is being passed from the browse modal to the vertical-insert operation. This data is passed to the vertical-insert operation via the model property on the operation data, the {{ and }} indicate that the value should come from the previous operation.

{
    "image-figure-insert": {
        "label": "Figure with image",
        "description": "A figure that groups an image with its title and description.",
        "steps": [
            {
                "type": "operation/open-image-browser-for-insert"
            },
            {
                "type": "operation/vertical-insert",
                "data": {
                    "model": {
                        "href": "{{permanentId}}"
                    },
                    "childNodeStructure": [
                        "fig",
                        [
                            "title",
                            [
                                {
                                    "bindTo": "selection",
                                    "empty": true
                                }
                            ]
                        ],
                        [
                            "desc"
                        ],
                        [
                            "image",
                            {
                                "href": {
                                    "bindTo": "href"
                                }
                            }
                        ]
                    ]
                }
            }
        ]
    }
}

Removing structures

Most inline elements can be removed by using the backspace key when the cursor is positioned after these elements. Other elements can be deleted by selecting them via the breadcrumbs and pressing backspace or delete. Alternatively, a delete operation can be offered via the element's contextual menu. For this, the platform offers the delete-node operation.

The operation in this example will be triggered via a context menu. This means that the contextNodeId is being set implicitly.

{
    "syntaxdiagram-delete": {
        "label": "Remove syntax diagram",
        "description": "Remove this syntax diagram.",
        "icon": "times",
        "steps": [
            {
                "type": "operation/delete-node"
            }
        ]
    }
}

When the node that should be removed is not the one containing the selection directly, a transform can be added to set the contextNodeId to a different node.

{
    "plentry-delete-as-row": {
        "label": "Remove row",
        "description": "Remove this row from the parameter table.",
        "icon": "times",
        "steps": [
            {
                "type": "transform/setContextNodeIdToSelectionAncestorMatchingDitaClass",
                "data": {
                    "parentDitaTypes": [
                        "pr-d/plentry"
                    ]
                }
            },
            {
                "type": "operation/delete-node"
            }
        ]
    }
}

Table operations

The platform offers a range of operations for working with tables. These are obviously only relevant when configuring an application which supports at least one table standard.

Inserting table

Inserting a table can be done with the table-insert operation. This operation expects the childNodeStructure to be set. It needs this structure to determine which table structure to use. In this example, the table-insert operation is used to insert a CALS table.

The CALS standard defines the table element as being some sort of figure-like element containing a tgroup element, the actual table itself. If this were to be a xhtml-table-insert operation, the value of the childNodeStructure would just have been "table". See the table-insert API page for more information.


{
    "cals-table-insert": {
        "label": "CALS table",
        "description": "A generic CALS table",
        "icon": "table",
        "initialData": {
            "childNodeStructure": [
                "table",
                [
                    "tgroup"
                ]
            ],
            "rows": 1,
            "columns": 1
        },
        "steps": [
            {
                "type": "operation/table-insert"
            }
        ]
    }
}

Adding and removing rows/columns

Adding and removing the rows or columns of tables can be done with the following operations:

These operations can be used directly and do not need any configuration. All these operations are available in their contextual variant:

These operations assume the contextNodeId to be set, which is implicitly done when used in a contextual menu.

Merging cells

Merging cells in any direction can be done with the following operations (if the used table standard supports merged cells):

These operations can be used directly and do not need any configuration.

These operations assume the contextNodeId to be set, which is implicitly done when used in a contextual menu.

Table styling

Some table specs support styling in the form of cell borders and header rows.

Contextual variants are also available:

Removing tables

Removing tables can be done by using the table-delete operation.

Custom mutations

Use a custom mutation if none of the existing operations meet the requirements. See addCustomMutation for more information on how to make a custom mutation.

Triggering operations

There are multiple ways to trigger the execution of an operation. The most obvious way is to click a button in the masthead. Another way is to click a button in the context menu. An operation can also be triggered by keyboard shortcuts when it is configured to do so.

See the "Creating a masthead" guide for more information about creating a masthead and binding operations to the buttons. See configureContextualOperations for more information about adding a contextual operation to an element.

Masthead

The most obvious way of triggering an operation is by clicking a button in the masthead of the application. Most of the insert-like operations should be located in the masthead, grouped by use. These operations are executed with an empty stepData.

Contextual menu

Another way of triggering an operation is by clicking a button in a context menu. These menus can be triggered by right-clicking elements which are configured to have a context menu, or by clicking the elementMenuButtonWidget. Operations which are triggered using a contextual menu are executed with a contextNodeId property on the stepData. The contextNodeId property is a NodeId which can be used in inline XPath expressions in an operation and resolved using the getNodeById method on DocumentsManager.

Contextual operations may be configured using configureProperties, using the contextualOperations property.

Keyboard shortcut

An operation can also be triggered by keyboard shortcuts when it is configured to do so. Generally, operations used for inline formatting are triggered with keyboard shortcuts. These operations are executed with an empty stepData.