Lurch web app user interface

Class

Atom

For information about the concept of atoms in Lurch in general, see the documentation of the Atoms module. Because atoms are HTML elements, their API is that provided by the browser for all HTML elements, and is not specific to their role as atoms. To provide an API that makes it easier to deal with atoms in a Lurch document, we create this class.

One simply constructs an instance of this class, passing the corresponding HTML element from within the editor, along with the editor itself, and the resulting object provides an extensive API (documented below) for interfacing with the atom in a variety of ways useful for the Lurch app.

This is analogous to how we deal with shells in the editor, using the Shells class.

Constructor

new Atom(element, editor)

Construct a new instance of this class corresponding to the atom represented by the given HTMLElement. Recall that the purpose of this class, as documented above, is to provide an API for consistent and convenient use of atoms, an API that is not part of the HTMLElement API. Thus to use that API, you use this constructor and then call functions on the resulting object. The intent is for such instances to be ephemeral, in the sense that you can create one, use it, and let it be garbage collected immediately thereafter, with little performance cost.

Parameters

  • element HTMLElement

    the element in the editor (a span or div) representing the atom

  • editor tinymce.Editor

    the editor in which the element sits

Source

Methods

applyValidationMessage(message)

This function inspects the given Message, which the caller wants applied to this Atom. It determines which of its feedback contents, if any, should be displayed to the user. It then makes a call to setValidationResult() to display that feedback.

The default implementation is to just extract the first piece of feedback from the message other than a message about an undeclared variable and use it, or if there is no such feedback in the message, erase the feedback shown on this Atom. However, subclasses can override this default behavior if they have specific types of feedback that they want to prioritize, or they need to combine multiple types of feedback.

Parameters

  • message Message

    the message whose validation data should be used to decorate this Atom

Source

contextMenu(forThis)

The default context menu for an atom is just the context menu for its parent atom, if any, or the empty array otherwise. This makes it easy to include the context menu for all ancestor atoms in any atom, by just starting with the context menu of the superclass and then adding items.

Parameters

  • forThis Atom

    the atom that received the right-click action that led to the creation of the context menu

Source

dataChanged()

This function is a handler for whenever the metadata stored in this atom changes. Its default implementation is to do nothing, but having it here allows the validation module to replace this handler with one that clears all validation feedback. We do it this way, rather than importing the validation module and calling its "clear" function ourselves, because it prevents a circular dependency.

See

  • wasDeleted()

Source

edit() → {Promise}

This is a placeholder implementation of this method, to be sure that all Atom instances have one. In subclasses, the function should pop up an editor for the user to edit this atom, and return a promise that resolves to true if the user saves their edits, and false if they cancel. The user's edits should already be saved into the atom when the promise resolves to true. This placeholder implementation returns a promise that false immediately, as if the user canceled their edits instantaneously.

Returns

  • Promise

    a promise that resolves to false

Source

editThenInsert()

The standard way to insert a new atom into the editor is to create it off screen, open up an editing dialog for that atom, and then if the user saves their edits, insert the new atom into the document, in the final state that represents the user's edits. If, however, the user cancels their edit of the atom, don't insert anything into the document.

This function does exactly that, when called on an offscreen atom, passing the editor into which to insert the atom as the first parameter.

See

  • edit()
  • editThenInsert()

Source

fillChild(type, html)

Fill the child of the specified type with the given HTML content. If the child does not yet exist, create it before populating it. See the documentation for getChild() for an explanation of the children of atoms.

Parameters

  • type string

    which type of child to write to; see the documentation for getChild() for a list of the valid children types

  • html string

    the HTML code to use to fill the child

Source

getChild(type, createIfNeeded) → {HTMLElement}

An atom has the following three or four internal parts, called children. If the atom is inline (a span) then each of the children is represented as an inner span, but if the atom is a block (a div) then each of the children is an inner div.

  1. The "metadata" child, which exists only in block-type atoms, and can contain arbitrarily large HTML metadata, none of which is displayed in the document for the user to see. Clients should not read from or write to this HTML metadata storage directly, but should instead use the functions in this class designed for that purpose, including getHTMLMetadata() and setHTMLMetadata(). The reason that this exists only in block-type atoms is because one cannot put arbitrary HTML inside a span, as one can with a div.
  2. The "prefix" child, which is the first child that is visible to the user in the document. This may be omitted or empty, but in those situations where the atom should have some decoration on its left side (for inline atoms) or its top (for block atoms), such content can be placed in the prefix. The client rarely needs to write to the prefix directly, but can instead use the fillChild() function to do so. You can use this function to read from the prefix when needed.
  3. The "body" child, which is where the main content of the atom should go. For example, if the atom represents a mathematical equation, the typeset version of that equation can go here. Again, to specify the body content, use fillChild().
  4. The "suffix" child, which functions just like the prefix, except appears at the end (right side for inline atoms, bottom for block atoms). Again, to specify the suffix content, use fillChild().

When you call this function, both parameters are optional. If you do not specify the type of child, it defaults to the body, and if the child you request does not exist, it defaults to creating it for you. (In a brand new atom, there are no children; it is empty.) If you do not wish the child to be created, but rather receive undefined as the result if it does not exist, set the second parameter to false.

Parameters

  • type string body

    which type of child to fetch, must be one of "metadata", "prefix", "body", or "suffix" (default is "body")

  • createIfNeeded boolean true

    whether to create the child in question if it does not already exist (default is true)

Returns

  • HTMLElement

    the child of the given type inside this atom

Source

getHTML() → {string}

Get the HTML representation of this atom, as it currently sits in the document. There is no corresponding setHTML() function; instead, see fillChild().

See

  • getHTML()

Returns

  • string

    the HTML representation of this atom

Source

getHTMLMetadata(key) → {HTMLDivElement}

Look up an HTML metadata entry in this atom using the given key. In addition to the JSONable metadata that can be stored in any atom (as documented in getMetadata()), you can also store arbitrarily large amounts of HTML in block-type atoms (not inline ones). This is useful, for example, when storing an entire dependency's HTML inside the atom that has imported it; the data can be stored as DOM nodes rather than as a JSON-encoded HTML string. This makes it easier to use when doing computations that depend on the meaning of the dependency's content.

This type of metadata is stored inside the "metadata" child of the block-type atom, as documented here. The return value for this function will be an HTMLDivElement that sits inside that "metadata" child, and serves as a wrapper containing any number of HTML elements (or any large amount of text). While the return value is a single element, it is a wrapper that should be ignored, because its child node list is the actual value of the metadata that was looked up.

Parameters

  • key string

    the key whose HTML should be looked up

Returns

  • HTMLDivElement

    the element wrapping the corresponding value

Source

getMetadata(key) → {any}

Look up a metadata entry in this atom using the given key. Atoms can contain metadata mapping any string key to any JSONable value. Thus this function will return a JSONable object, as extracted from the given key, if that key indeed appears in this atom's metadata.

This type of metadata is stored in the dataset property of the HTMLElement representing the atom. This is a natural way to store small amounts of data about the atom. For larger amounts of data, you may wish to use getHTMLMetadata() instead.

Parameters

  • key string

    the key whose value should be looked up

Returns

  • any

    the data associated with the given key, or undefined if the key is not in the metadata

Source

getMetadataKeys() → {Array.<string>}

Look up the keys for all metadata entries stored in this atom. Atoms can contain metadata mapping any string key to any JSONable value. This function returns only the keys, as an array.

Returns

  • Array.<string>

    all keys under which metadata has been stored in this atom

Source

isEditable()

Atoms are always editable, unless they sit inside some DOM node that is marked as contenteditable=false. This function checks to see if that is the case, and returns true iff no ancestor DOM node has that property.

Source

parent() → nullable{Atom}

If this atom is contained inside another atom, then the innermost such atom is this one's "parent." This function returns that parent, if any, and null otherwise.

Returns

  • Atom

    the Atom that is the parent of this Atom, if any

Source

removeChild(type)

Remove the child with the specified type (or do nothing if there is no such child). See the documentation for getChild() for an explanation of the children of atoms.

Parameters

  • type string

    which type of child to write to; see the documentation for getChild() for a list of the valid children types

Source

setHTMLMetadata(key, value)

Store an HTML metadata entry in this atom under the given key. Block-type atoms can contain metadata mapping any string key to any amount of HTML content, as documented here. This function stores a new entry in that metadata storage area.

Note that only block-type atoms can contain HTML metadata, and so calling this function on an inline atom will throw an error.

Any HTML element passed as the value will not be used directly, but will be copied (i.e., its HTML code will be written to the .innerHTML property of the appropriate metadata element), in case the element in question is not from the same document. The value may instead be the HTML code itself, as a string.

Parameters

  • key string

    the key under which to store the value

  • value HTMLElement | string

    the value to store, either as an HTMLElement or a string

Source

setHoverText(textnullable)

Set (or clear) the hover text on this atom. Hover text is what's shown in a small popup when the user hovers over the atom in the editor.

Parameters

  • text string <nullable>

    the text to set as the hover text for this atom's HTMLElement, or null to remove the hover text

Source

setMetadata(key, value)

Store a metadata entry in this atom under the given key. Atoms can contain metadata mapping any string key to any JSONable value, so you should provide a value that is amenable to JSON encoding. It will be stored using its JSON encoding, as a string.

If the caller omits the value parameter, or sets it to undefined, this function does nothing.

Parameters

  • key string

    the key under which to store the value

  • value any

    the value to store

Source

setValidationResult(resultnullable, reasonnullable)

Set the suffix of the atom to reflect its validation result.

The first argument must be one of "valid", "invalid", "error", or "indeterminate". No error is thrown if you use another value, but it will not display any meaningful feedback icon. To clear all validation feedback, omit both arguments, or call setValidationResult(null). The meanings of these indicators are as follows.

  • "valid": the atom represents correct mathematical work
  • "invalid": the atom represents incorrect mathematical work
  • "indeterminate": the atom cannot be clearly classified as valid or invalid (e.g., the user has provided something vague, and their settings specify that such things should not be aggressively marked wrong, but just indeterminate, to request more specificity)
  • "error": the software encountered an internal error while attempting to validate the atom. (Obviously this is to be avoided! It is available to use if an internal error occurs, so that the user gets truthful feedback in such a case, and can report the bug so that we can set about trying to fix it. But of course we strive to write software does not encounter errors, and thus in which this type of feedback is never actually seen by a user.)

The second argument should be the text to be shown when the user hovers their mouse over the atom. You can omit this if you do not want any such text, but it is recommended to always have such text, for the user's benefit. Note that browsers do not support HTML tags in such hover text panels, but they do support newline characters ("\n").

Parameters

  • result string <nullable>

    the validation result, one of "valid", "invalid", "error", or "indeterminate"; can be omitted in order to clear all validation feedback

  • reason string <nullable>

    the reason for the validation result, as text to be displayed when the user hovers their mouse over the atom

Source

toLCs() → {Array.<LogicConcept>}

This is a placeholder implementation of this method, to be sure that all Atom instances have one. In subclasses, the function should return an array of LogicConcepts that represent the meaning of this atom. If the atom has no meaning in terms of LogicConcepts, simply return an empty array. That is the default implementation.

Returns

  • Array.<LogicConcept>

    an empty array of LogicConcepts, in this default implementation

Source

toLatex() → {string}

All atoms must be able to represent themselves in LaTeX form, so that the document (or a portion of it) can be exporeted for use in a LaTeX editor, such as Overleaf. The default implementation, defined here in the Atom class, is to return a string of the form "(Lurch X)", where X is the type of the atom. For example, "(Lurch expression)". Subclasses are likely to override this with something more suitable and useful in a LaTeX document.

Returns

  • string

    default LaTeX representation of the atom

Source

update()

This is a placeholder implementation of this method, to be sure that all Atom instances have one. In subclasses, the function should update the visible representation of the atom in the document based on the attributes stored in its metadata. The default implementation does nothing.

Source

wasDeleted()

This function is a handler for whenever an atom has just been deleted from the document. Its default implementation is to do nothing, but having it here allows the validation module to replace this handler with one that clears all validation feedback. We do it this way, rather than importing the validation module and calling its "clear" function ourselves, because it prevents a circular dependency.

See

  • dataChanged()

Source

static

allElementsIn(editor) → {Array.<HTMLElement>}

Find all elements in the given TinyMCE editor that represent atoms, and return each one in the order they appear in the document.

Parameters

  • editor tinymce.Editor

    the editor in which to search

See

Returns

  • Array.<HTMLElement>

    the array of atom elements in the editor's document

Source

static

allIn(editor) → {Array.<Atom>}

Find all elements in the given TinyMCE editor that represent atoms, and return each one, transformed into an instance of the Atom class. They are returned in the order they appear in the document.

Parameters

  • editor tinymce.Editor

    the editor in which to search

Returns

  • Array.<Atom>

    the array of atom in the editor's document

Source

static

create(editor, tagName, content, metadata) → {Atom}

Create a new atom element, as in createElement(), then fill its body with the given content, optionally set one or more metadata key-value pairs, and return an Atom instance corresponding to the new element. Note that this does not insert the element anywhere into the editor.

Parameters

  • editor tinymce.Editor

    the TinyMCE editor in which to create the element

  • tagName string

    the tag to use, "span" for inline atoms or "div" for block-type atoms (which is the default)

  • content string

    the HTML code to use for filling the body of the newly created atom

  • metadata Object

    a dictionary of key-value pairs to store in the metadata of the newly created atom (defaults to the empty object { }, meaning not to add any metadata)

Returns

  • Atom

    the Atom instance corresponding to the newly created atom element

Source

static

createElement(editor, tagName) → {HTMLElement}

Create an HTMLElement that can be placed into the given editor and that represents an inline or block-type atom, as specified by the second parameter. The element will be given an HTML/CSS class that marks it as representing an atom, and will be marked uneditable so that TinyMCE will not allow the user to alter it directly.

Parameters

  • editor tinymce.Editor

    the TinyMCE editor in which to create the element

  • tagName string div

    the tag to use, "span" for inline atoms or "div" for block-type atoms (which is the default)

Returns

  • HTMLElement

    the element constructed

Source

static

findAbove(node, editor) → nullable{Atom}

When a mouse event takes place in the document, it is useful to be able to check whether it happened inside an element representing an atom. So this function can take any DOM node and walk up its ancestor chain and find whether any element in that chain represents an atom. If so, it returns the corresponding Atom instance. If not, it returns null.

Parameters

  • node Node

    the DOM node from which to begin searching

  • editor tinymce.Editor

    the editor in which the node sits

Returns

  • Atom

    the nearest Atom enclosing the given node

Source

static

from(element, editor) → {Atom}

Instead of the Atom constructor, use this function to convert an element in the document into a functioning Atom instance. The reason you should use this function is because the Atom constructor always creates an Atom instance, but this function may create an instance of an Atom subclass, if that's what the element represents. Thus the resulting object will have more specialized functionality suitable to the type of atom in question. This behavior is powered by the registration of Atom subclasses using registerSubclass().

Parameters

  • element HTMLElement

    an element that has passed the check in isAtomElement()

  • editor tinymce.Editor

    the editor in which the element sits

Returns

  • Atom

    the atom represented by the element

Source

static

isAtomElement(element) → {boolean}

One can construct an instance of the Atom class to interface with an element in the editor only if that element actually represents an atom, as defined in the documentation for the Atoms module. This function checks to see if the element in question does. To create elements that do represent atoms, see createElement().

Parameters

  • element HTMLElement

    the element to check

Returns

  • boolean

    whether the element represents an atom

Source

static

newBlock(editor, content, metadata) → {Atom}

A convenience function that calls create() for you, but uses the tag "div" to make an inline element. This makes the client's code more clear, because it specifies that we are creating a new block-type atom.

Parameters

Returns

Source

static

newInline(editor, content, metadata) → {Atom}

A convenience function that calls create() for you, but uses the tag "span" to make an inline element. This makes the client's code more clear, because it specifies that we are creating a new inline atom.

Parameters

Returns

Source

static

registerSubclass(name, subclass)

This class tracks its collection of subclasses so that elements in the editor can have an appropriate Atom subclass wrapper created around them as needed, for custom event handling. To register a subclass, call this function. To create an atom that has the right subclass, see from().

Example:

class Example extends Atom { ... }
Atom.registerSubclass( 'Example', Example )

Parameters

  • name string

    the name of the subclass to register

  • subclass Object

    the subclass itself

Source

static

simplifiedHTML() → {string}

When embedding a copy of the Lurch app in a larger page, users will want to write simple HTML describing a Lurch document, then have a script create a copy of the Lurch app and put that document into it. This function can convert any HTML, including HTML that has atom elements in it, into that simplified HTML that is more human-readable, yet still describes a Lurch document.

Returns

  • string

    the representation of the atom as a lurch element

Source

static

unsimplifyDOM(node, editor)

Traverse a DOM tree and convert any simplified HTML elements in it into HTML elements that represent atoms (or shells). For example, an HTML element of the form <latex>...</latex> will be replaced with the full HTML code for a ExpositorMath atom as it sits in a Lurch application's document.

Parameters

  • node Node

    the DOM node to use as the root of the traversal; it is modified in-place

  • editor tinymce.Editor

    the editor in which the modified DOM will eventually be placed or copied

Source