The JavaScript code works by DOM manipulations of
a top-level div
element, and assume that element
has a certain well-behaved form.
<div class="domterm">
domterm-toplevel </div>
A top-level “DomTerm window” is a <div>
element
whose class
is domterm
.
You don’t need to create domterm-toplevel - it is created and managed by DomTerm, based on data from the back-end.
<div class="dt-buffer" buffer="name">
block-content* </div>
There are one or two domterm-buffer elements,
for respectively the “normal” and “alternative” screen buffer.
These contain all the “actual” content.
If there is a single buffer, the name is "main only"
;
if there are two buffers, the respective values for name
are "main"
and "alternate"
.
One or more invisible <div>
elements used by the implementation.
<div class="domterm-spacer"/>
Blank space at the bottom, used to support scrolling.
<div>
block-content* </div>
| opaque-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.
<span>
line-content* </span>
| soft-nl | input-line | other-content<span line="hard">&#A;</span>
An explicit newline. Has a "\n"
text node as its sole child.
<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.
<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.
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.
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.
A command group is the set of input and output lines for a single user command.
<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.
<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.
<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.
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 elements 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.
Xterm supports an “alternate screen buffer” which is used
by character-based programs like emacs.
DomTerm allows an arbitrary number of buffers
(not just two), with the most recent (bottom-most) being the active one.
Each buffer is a <div class="dt-buffer">
element.
An alternate buffer has a buffer="alternate"
attribute.
It is the sibling of the normal screen buffer,
which has a buffer
attribute valued "main"
.
The "dt-buffer"
element contains one or more logical-lines.
Returning to the normal screen deletes the <div>
for the
alternative screen, along with all of its contents,
and changes the buffer
attribute back to "main only"
.
<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 per-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">text"</span>
Specify extra indentation to add to following continuation lines
(in current group). (These pprint-indent
elements are non-visible.)
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 text children 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
.
<span line="kind" [breaking="yes"]> [pre-break-child] [non-break-child] [newline] indentation* [post-break-child] </span>
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>
<span class="pprint-indentation">..</span>
| textA generalized optional line-break, where kind
is one of "linear"
, "fill"
, "miser"
(currently same as "fill"
), or "required"
.
The breaking
attribute is set when a line-break should be shown;
the attribute is removed when the break is no longer wanted.
You can specify contents to
insert before the break (when there is a break)
with a pprint-pre-break
element.
Similarly, content after the break
can be specified using a pprint-post-break
element.
To specify contents to be used when there is no break,
use a pprint-non-break
element.
A newline is inserted when the line is actually breaking
(though not for soft
line-breaks).
The indentation, if any, is a mix of cloned outer pprint-indent
elements and text that represent actual calculated indentatation.
They automatically inserted before the post-break-content
when the line is actually breaking,
and removed if the line-break is removed.
A pprint-indentation
element is a (visible) clone
of a (non-visible) outer pprint-indent
element.
<span std="caret">
caret-text</span
<span std="caret" value="
caret-text"></span
This is used to display the caret position. If the caret is a block
or underline, then the caret-text is the character at the caret
that is styled with inverse video or an underline.
If the caret is at the end of the line, then the caret-text
is a single space, and we use a value
attribute, because the
space is not actually part of the line.
If the caret is followed by other text (on the same line),
then the caret-text
is a text node consisting of the
first character of the following text (which is removed from
the following text).
If the caret is a vertical-bar then there may still be a caret
element,
but the caret-text is empty.
If you type a printing character (while in char-mode), DomTerm
sends the character to the process, which will then echo (display) it.
If there is a slow network connection the delay between when you type
the character and the echo can be disconcerting and slow down your typing.
To avoid this, DomTerm will tentatively display it right after
you type it, without waiting for the echo.
The tentative text is displayed in a pending
element, as
a new-text text child:
<span class="pending" old-text="old-text">
new-text</span>
Text (specifically the new-text) is by default displayed with a light gray background to show that it is tentative.
Typing the Left or Right arrow keys may create a Pending
element where both the old-text and the new-text
are the same string - the text that was moved over.
Typing Delete or Backspace has just an old-text attribute
for the text that has been (tentatively) deleted.
Either Left or Backspace adds a direction="Left"
attribute;
either Right or Delete adds a direction="Right"
attribute.
There is usually at most a single pending
element, but in principle
there can be many. A pending
node may contain children that are nested
pending
or std="caret"
elements.
Before output is processed (or after a timeout) all existing pending nodes are “undone” (in reverse order) so the DOM state matches that expected by the application: All new-text elements are reverted back to the old-text values, and the caret may also be adjusted. Typically the received output wil be “echo” that has the effect of updating the state to match the pending operations - but now as definite updates from the application, rather than pending.