DOM structure

The JavaScript code works by DOM manipulations of a top-level div element, and assume that element has a certain well-behaved form.

domtern-element ::= <div class="domterm"> domterm-toplevel </div>

A top-level “DomTerm window” is a <div> element whose class is domterm.

domterm-toplevel ::= internal-div-elements block-content*

You don’t need to create domterm-toplevel - it is created and managed by DomTerm, based on data from the back-end.

Currently, the block-content is a single <div class="interaction"> with an id attribute of the form xxx_main. This is the xterm “normal screen buffer”.

More structure will be supported, for example individual <div> to wrap each command and its output.

internal-div-elements

One or more invisible <div> elements used by the implementation.

block-content ::= logical-line | <div> block-content* </div> | opaque-line
logical-line := <div class="domterm-pre"> line-content* hard-nl </pre>
   | <pre> line-content* hard-nl </pre>
   | <p> line-content* hard-nl </p>

A logical-line is one or more “rows” (separated by soft-lines), consisting of character and other inline data, ending with hard-nl representing an explicit newline.

The intent is that <div class="domterm-pre">, <pre> and <p> are treated logically the same, but <div class="domterm-pre"> (or <pre>) will be monospace, while <p> can use other fonts and styling. The line-breaking algorithm uses the width of the line, not the number of characters, so it should also work for <p> elements.

Normal terminal output will create <div class="domterm-pre"> elements, rather than <pre>, because Copy command on some browsers (at least current Firefox) adds extra newlines.

line-content ::= text | <span> line-content* </span> | soft-nl | input-line | other-content
hard-nl ::= <span line="hard">&#A;</span>

An explicit newline. Has a "\n" text node as its sole child.

soft-nl ::= <span line="soft"></span>

An implicit newline, inserted by DomTerm when a line overflows. Has no explicit text content, but CSS adds a "\n" as before content. CSS by default also add a continuation arrow.

input-line ::= <span id="input1" std="input" contenteditable="true"> text </span>

The location of the input cursor. In char-mode the text is empty. In line-mode contains the current input line. Also used for previous input lines, but without contenteditable set. Referenced by the inputLine field of the DomTerm object.

opaque-line

A non-empty block-level element that isn’t navigable at the level of rows and columns. For example a <table>. It is treated as a single empty line.

Lines and columns

This section discusses the mapping between the nested DOM structure and the terminal’s lines and columns.

In a well-formed tree, all line-breaks have the form of <span> element with a line attribute. The child of a <span> element is either a text node consisting of only a newline, or there are no children (and a newline is added using CSS styling). Either way it is treated as a single line-break.

Inserting HTML may also include <br> elements and text containing newlines. These are not handled consistently. Ideally, we should convert newlines in pre-style elements to <span line="hard"> elements.

The last child of a non-empty block-level element (<div>, <pre> etc) should be either another block-level element or a line-break. (We could also allow a <span> whose last element is a line-break, but isn’t clear if this is useful or desirable.) Inserting HTML should insert if needed final line-break (though it currently doesn’t). Alternatively, we can treat a missing line-break as an implicit line-break; currently this is not handled consistently.

A “line” is then the text and elements between two (explicit or implicit) line-breaks.

“Opaque elements” include <img>, <svg>, <object>, and <iframe>. These are treated as single character, taking up a single “column”.

The “characters” of a line are those in text nodes in the line, plus opaque elements.

Command groups

A command group is the set of input and output lines for a single user command.

command-group ::= <div class="command-group"> command-input-line+ command-output? </div>

Usually there is a single command-input-line, but there may be more than one if there are continuation lines.

command-input-line ::= <pre> <span std="prompt"> prompt-text </span> <span std="input"> input-text </span> </pre>

A command-input-line is logical-line which (at least normally) has the form of a prompt followed by the typed input command.

command-output ::= <div class="command-output"> block-content+ </div>

The output from the command. Normally, each block-content is a logical-line. The <div class="command-output"> element may have the domterm-hidden attribute if it is hideable.

Hide/show buttons

A hide/show button is a clickable toggle “button” that controls whether certain “associated output” is shown or hidden. For example the “associated output” of a shell command could be the set of output lines from the command. Initially, the output is in the shown state, and the button displays a “hide” icon. Clicking the button will hide output lines, as well as changing the button to display a “show” icon. Clicking the button again changes the icon to the “hide” icon and unhides the output lines.

<span std="hider" [domterm-hiding="true" or "false"]> hide-icon </span>

This is a hide/show button. The hide-icon is either empty or one of the strings with odd-numbered index in DomTerm’s showHideMarkers property. The value of the showHideMarkers property is an array of strings, where the even-numbers elements are “show” icons, and the following odd-numbered elements are the corresponding “hide” icons. It is suggested (but not required) that these icons be single graphic characters. Good choices are "\u25B6" ( “black right-pointing triangle”) for “show”, and "\u25BC" ( “black down-pointing triangle”) for “hide”.

The attribute domterm-hiding must be "true" or "false"; if missing it defaults to "false". Its value is flipped on each click,

If you are unsatisfied with the existing icon choices you can either set showHideMarkers to other strings, or you can use CSS to change the look of the icons. For example to use [-] and [+] use these CSS rules:

span[std="hider"][domterm-hiding="true"]:after { content: "[+]" }
span[std="hider"]:after { content: "[-]" }

In this case, you probably want the hide-icon text in the <span> to be empty.

The “associated output” for a hide/show button is the set of sibling elements following the button, as well as sibling elemets of the button’s parent (assuming that parent is a <pre> or <p> element). Only elements that have the domterm-hidden attribute are affected. Hiding is done by changing the value the domterm-hidden from "false" to "true"; un-hiding changes it back to "false". This is using a CSS style rule that sets the display property of an element to none when domerm-hidden is true

A future extension would allow lazy associated output: The initial state is hidden, and the back-end does not provide the output until it is requested, by clicking the hide/show button. Lazy output is useful for displaying and inspecting large (or even infinite) data structures, such as as a directory hierarchy or a complex object graph.

Alternate screen buffer

Xterm supports an “alternate screen buffer” which is used by character-based programs like emacs. Switching to the alternate buffer creates a new <div class="interaction>, with an id attribute of the form xxx_alternate. It is the sibling of the normal screen buffer (with id attribute of the form xxx_normal). This <div> contains one or more logical-lines. Returning to the normal screen deletes the <div> for the alternative screen, along with all of its contents.

Pretty-printing and line-breaking

<span class="pprint-group"> contents </span>

A “logical block” of content that should be printed together, on the same line, if possible. If there is a prefix, it precedes the "pprint-group" element. If there is a per-line prefix, it is in a <span class="pprint-prefix"> preceding element. If there is a block suffix, it follows the "pprint-group" element.

<span class="pprint-prefix"> per-line-prefix </span>

A pre-line prefix. This must be the previous sibling of a <span class="pprint-group"> element. It is displayed just before the logical group, and also for each continuation line, at the same indentation.

<span class="pprint-indent" delta="num-chars" />
<span class="pprint-indent" block-delta="num-chars" />
<span class="pprint-indent" indentation="text" />

Add extra indentation to following continuation lines. A delta="num-chars" changes the indentation to num-chars characters relative to the current horizontal position. A block-delta="num-chars" changes the indentation to num-chars characters relative to the start of the current logical block. Specifying indentation="text" specifies text as an extra per-line prefix. The extra indentation is reset at the end of the logical block.

<span line="kind" />

Represents a conditional newline of the specified kind, which can be fill, linear, miser (currently the same as fill), or required.

indentation ::= <span class="pprint-indentation">..</span>

These represent actual calculated indentatation. They are only inserted when if a line-break is inserted, and removed if the line-break is removed.

<span line="kind" line-attribute... > [pre-break-child] [non-break-child] [indentation] [post-break-child] </span>
line-attribute ::= breaking="yes|no" | pre-break="pre-break-text" | post-break="post-break-text"
pre-break-child ::= <span class="pprint-pre-break">contents</span>
post-break-child ::= <span class="pprint-post-break">contents</span>
non-break-child ::= <span class="pprint-non-break">contents</span>

A generalized optional line-break. You can specify contents to insert before the break (when there is a break) either with a pre-break attribute or with a pprint-pre-break element. Similarly, content after the break can be specified using either a post-break attribute or a pprint-post-break element. (The indentation, if any is automatically inserted before the post-break-content.) To specify contents to be used when there is no break, use a pprint-non-break element.