bd-core [namespace]

The Backdraft Library

Description

Backdraft is a library for building browser-hosted user interfaces. It is fanatical about four key design values:

Pure JavaScript

Backdraft-defined components are expressed in pure JavaScript. There is no markup (HTML, JSX, etc.); there is no templating. A compilation/built step is never required. This makes development easier and faster.

Unopinionated

The Backdraft programming model completely separates the user-interface from program logic. Backdraft components work with any application design model because they do not impose any constraints on the design model. This decreases program construction complexity by definitively separating concerns.

Embrace the DOM

Modern DOM is powerful. There is no need to add abstraction layers like virtual DOM or synthetic event systems. Backdraft includes direct, unfiltered access to the DOM, but separates these details from program logic...once again, decreasing construction complexity by separating concerns.

Self-contained, Minimal, and Small

Backdraft has no dependencies and requires no special tooling. It provides precisely the machinery necessary to build user-interface components quickly and efficiently, and no more. It is small and easy to grok. This makes it easy to learn and extend.

Requirements

Backdraft is built with 100% modern Javascript (ES6). It requires a modern browser:

  • Chrome v69+
  • Firefox v62+
  • Safari v12+
  • Edge v42+
  • iOS v10+

If you want to target some old, decrepit browser, build your code in a modern environment and then cross-compile it to target the old environment. Here's a good reference to start you on this task. Note that Backdraft is intended to build user interfaces for applications--not advertising web pages. Most users of applications have access to a modern browser so, in the normal case, this is never an issue.

Installation and Basic Usage

See Installation for installation basic usage instructions.

Package Organization

Backdraft is distributed with three versions of the code:

  • the root module of the raw source is located at package-root/lib.js; each source file is an ES6 module
  • a rollup of the library into a single ES6 module is located at package-root/dist/lib.js
  • a rollup of the library into a single UMD module is located at package-root/dist/lib-umd.js; when this module is injected into an HTML document with a script element, it defines the global variable bd which contains all of the Backdraft public functions, classes, and variables

classes [namespace]

classes defined by the Backdraft Library

functions [namespace]

functions defined by the Backdraft Library

mixins [namespace]

mixin classes defined by the Backdraft Library

Description

A mixin is a function that follows this pattern:

function mixin(superClass) {
    return class extends superClass {
        // properties and methods added by mixin
    };
}

The key point of the mixin pattern is that the mixin can be defined before the class that it extends (its superclass) is defined. This pattern is discussed in more detail in Simple Mixins.

Mixin classes can be used to simulate multiple inheritance. For example Backdraft defines the mixin classes watchHub and eventHub. watchHub and eventHub are completely independent of each other. Indeed, there are cases where a new class would like to use either one but not the other as a base class. Clearly it would be a design error to have them serve in a superclass-subclass relationship. On the other hand Component requires both classes' features. Since they are both mixins, all of these problems are easily solved:

class SomeClassWithWatchHub extends watchHub(class{}){
    // properties and methods for SomeClassWithWatchHub
}

class SomeClassWithEventHub extends evehtHub(class{}){
    // properties and methods for SomeClassWithEventHub
}

class Component extends watchHub(eventHub(class{})) {
    // properties and methods defined by Component
}

For Component, eventHub extends class{}, watchHub extends eventHub, and Component extends watchHub. The prototype chain looks like:

Component -> watchHub -> eventHub -> class{}

Since watchHub and eventHub are both mixins, they are available for use in client-defined classes that do not derive from Component.

Mixin classes work almost the same as normal classes with one notable exception: instanceof does not work. For example, even though Component has all of the properties and methods of watchHub, an instance of Component is not an instance of eventHub since eventHub is not a constructor function (it is a function that returns a constructor function). To solve this problem, mixins typically provide a property isUnique-mixin-name that returns true. For example, eventHub defines the property eventHub.isBdEventHub:

class MyClass extends eventHub() {
}

let instance = new MyClass();
instance instanceof MyClass;  // true
instance instanceof EventHub; // false
instance.isBdEventHub;        // true

instance new Component(); // Backdraft Component baseclass
instance instanceof Component;  // true
instance instanceof EventHub; // false
instance instanceof WatchHub; // false
instance.isBdEventHub;        // true
instance.isBdWatchHub;        // true

post-processing-functions [namespace]

post-processing functions available during rendering

Description

When a component is rendered, the Element tree returned by Component.bdElements is transformed into a tree of DOM nodes and Component instances as given by the nodes in that tree. The transformation for each node takes three steps:

  1. Instantiate the DOM node or component instance. Important: when describing the operation of post-processing functions, this node/instance is termed the ppfTarget.
  2. Initialize the properties of the new node/instance as given by Element.ctorProps (note: if a new component instance is being created, this step is actually part of Step 1 since ctorProps are passed as constructor arguments when the component is created).
  3. Apply the post-processing functions to the ppfTarget as given by Element.ppFuncs.
  • the component instance whose Element tree is being rendered is termed the ppfOwner
  • the particular node in that tree that is being instantiated, initialized, and post-processed with the post-processing functions is termed the ppfTarget
  • each key in Reflect.ownKeys(ppFuncs) is termed a ppfProcId
  • each ppfProcId identifies a post-processing function that is located at getPostProcessingFunction(ppfProcId); this function is termed a ppfProc
  • each value at ppFuncs[ppfProcId] is termed the ppfArgs for the ppfProc given by ppfProcId

A post-processing function has the signature (ppfOwner, ppfTarget, args). if ppfArgs is an array, then it is spread when it the ppfProc is applied. For example, if ppfArgs is [arg1, arg2, arg3] for a particular ppfProcId, then getPostProcessingFunction(ppfProcId)(ppfOwner, ppfTarget, ...ppfArgs) is applied. Let's look at an example to drive this all home:

class MyButton extends Component {
    bdElements(){
        return e("div", {
            className: "bd-button",
            bdAdvise: {click: this.onClick.bind(this)},
            bdReflect: {innerHTML: ["label", label => label.toUpperCase()]}
        });
    }
}

let button = new MyButton({});
button.render();

For the node e("div", ...),

  • bdAdvise and bdReflect are ppfProcIds and the indicate the post-processing functions getPostProcessingFunction("bdReflect") and getPostProcessingFunction("bdReflect"), respectively.
  • button is the ppfOwner when the post-processing functions are applied
  • the DIV DOM node is the ppfTarget when the post-processing functions are applied
  • bdAdvise has ppfArgs === {click: this.onClick.bind(this)}; bdReflect has ppfArgs === {innerHTML: ["label", label => label.toUpperCase()]}.

When button is rendered, Component retrieves button's Element tree by applying bdElements. The tree contains a single DIV node with a ctorProps value of {className: "bd-button"} and a ppFuncs value of {bdAdvise: {...}, bdReflect: {...}}. After the DIV node is created (Step 1) and the className property set to "bd-button" (Step 2), the post-processing functions bdAdvise and bdReflect are applied. For example bdAdvise will be processed as follows:

getPostProcessingFunction("bdAdvise")(button, node, {click: this.onClick.bind(this)})

If you look at the documentation for bdAdvise, you'll see that this statement results in the following:

button.ownWhileRendered(connect(node, "click", this.onClick.bind(this)))

Looking back at the definition of the Element node, you will notice that there is one Hash that holds both ctorProps and ppFuncs. The element factory e determines whether or not a particular property in the hash is a post-processing function by checking for its existence in getPostProcessingFunction.

ppfArgs is often a single Hash; further, actual ppfArgs values are often a Hash with a single property. Backdraft supports some syntax sugar, termed the underscore optimization, to improve the expression in such cases:

{somePpf: {foo: "someArg"}}
{someOtherPpf: {bar: [x, y, z}}

are equivalent to

{somePpf_foo: "someArg"}
{someOtherPpf_bar: [x, y, z]}

types [namespace]

types defined by the Backdraft library

variables [namespace]

variables defined by the Backdraft Library

Collection [class]

manages a homogeneous collection of children

Superclasses

Componentbase class used to build a user interface components

Description

Collection defines the property collection (an array). When rendered, Collection manages a collection of homogeneous child components to ensure there is a 1-to-1, onto mapping between each item in collection and each child component. If the collection is watchable, then if/when the array changes size, the rendered children components are automatically adjusted (inserted/deleted) to maintain the 1-to-1, onto mapping.

Collection also signals children of certain state changes in the underlying data:

  • If the length of the collection changes, the the optional child method onMutateCollectionLength is applied (if it exists) and any watchers connected to the child's collectionLength property are signaled.
  • If the index of the particular item to which a particular child is associated changes, then the optional child method onMutateCollectionIndex is applied (if it exists) and any watchers connected to the child's collectionIndex property are signaled.
  • If the item to which a particular child is associated changes, then the child's item property is mutated.

Collection is very frugal in mutating the item associated with a child. When the underlying data is re-ordered or items are inserted/deleted, the children associated with existing items are not destroyed and re-created.

The Backdraft Polygraph example uses Collection data extensively. You can explore Polygraph in this Pen or load the example directly into your browser.

CollectionChild [class]

a base class for a child of Collection

Superclasses

Componentbase class used to build a user interface components

Description

CollectionChild provides core machinery for components intended to be used as one of a collection of homogeneous component instances created consequent to a homogeneous collection of data actualized by an array.

CollectionChild takes the keyword constructor arguments parent and collectionIndex. parent gives the component instance that is creating and will contain the CollectionChild instance. CollectionChild assumes parent maintains the underlying data collection at parent.collection. The keyword argument collectionIndex gives the index into parent.collection of the data item that is associated with the particular CollectionChild instance.

The core functionality of CollectionChild is to provide, in coordination with a parent like Collection, the watchable properties collectionItem, collectionIndex, and collectionLength, which reflect the data item, data item index, and collection associated with the instance. Further, withWatchables provides machinery to automatically provide watchable properties that reflect properties in collectionItem.

CollectionChild assumes the parent will automatically signal collectionLength property mutations on the CollectionChild instance; this is the case with Collection. If the collection array changes in the parent (that is, a different array is set compared to a mutation on the same array), the the parent is responsible for setting the collectionItem. Otherwise, CollectionChild automatically watches underlying data referenced by collectionItem and signals its own mutations accordingly.

The Backdraft Polygraph example uses CollectionChild extensively. You can explore Polygraph in this Pen or load the example directly into your browser.

Component [class]

base class used to build a user interface components

Superclasses

eventHubprovides machinery to signal events
watchHubprovides machinery to signal mutations within an object

Description

Component abstracts a user interface component. Subclasses derived from Component provide a public interface that is independent of the DOM, and Component provides machinery to aid in constructing the protected implementation that bridges the gap between that public interface and the DOM.

Consider the example of a combo box component. From the view of the application logic, a combo box is simply an input mechanism to collect a value from a list of (value, choice-text) pairs:

e(ComboBox, {
    list: someList, value: initialValue, watch: {value: valueWatcher}
});

The protected implementation of ComboBox constructs the DOM tree that presents the user interface and responds to DOM events as the user interacts with the component. For an example of a complete combo box implementation see bd-widgets.ComboBox.

Component provides machinery to aid in constructing the protected implementation, including:

  1. Declarative composition
  2. Life cycle management
  3. Child management
  4. Property managment
  5. Event management
  6. Focus management
  7. IO state and behavior management

Subclasses derived from Component are intended to be based on composition and specialization patterns; a Component subclass can be defined by:

  • composing several other component subclasses
  • specializing/deriving from/subclassing another component class
  • any combination of the above

Declarative Composition

The actualization of a browser-hosted user interface component is a DOM tree or forest. Component uses declarative composition to describe the structure, properties, and connections of the DOM tree/forest prescribed by a particular Component subclass. This is to say that the tree/forest is functionally declared as as an abstraction as opposed to being built with actual DOM nodes by an imperative process, and the components used in the declaration may be other Component subclasses. Here is an example:

e("div", {className: "renameDialog"},
    e("div",
        e(Labeled, {label:"Current Name", value: this.currentName}),
        e(Labeled, {label:"New Name", value: e(Input, {size:50, bdReflect:"name"})})
    ),
    e("div", {className: "bottomButtons"},
        e(Button, {label: "Cancel"}),
        e(Button, {label: "OK"})
    )
);

The example is from a rename dialog that shows a current name, collects a new name, and provides "OK" and "Cancel" buttons to terminate the dialog. See the Rename Dialog Example for a working example.

The tree is an abstraction of the the actual DOM tree associated with a particular Component instance. The abstraction is composed of Element nodes that contain other Element nodes; e is a factory function that creates such nodes.

  • Each node that has a tag name for the first argument to the e factory indicates a DOM element (all the "div" nodes).
  • Each node that has a constructor function for the first argument indicates a Component subclass (Labeled, Input, and Button).

Subclasses found in the tree will have their own trees of Element nodes: this is the composition aspect of the declarative composition design. Component's internal machinery transforms an abstract Element tree in to a tree of DOM nodes; this is described in detail below.

Component subclasses may define their DOM as either a single-rooted tree or a multi-rooted forest, and all Component machinery is designed and implemented to handle both cases. When rendered, a reference to the DOM created by a component is maintained at instance.bdDom.root (see bdDom). For a single-rooted tree, bdDom.root will reference a single DOM node; similarly, for a multi-rooted forest, bdDom.root will reference an array of DOM nodes. Unless there is an important difference between the behavior of a single-rooted tree compared to a multi-rooted forest, we shall write in terms of a single-rooted tree for the remainder of Component's reference documentation.

Notice that the entirety of this system resides within the standard JavaScript environment. There is never a need to "compile" or otherwise transform an Element tree.

Since the Element tree is declared with standard JavaScript, the full power of the language is available in the declaration. There is never the need to "escape" out of a templating/markup language to include state-dependent structure/value in the declaration. Here is another example where the structure of the declaration is affected by state:

e("div", {className:"welcome-message"},
    e("p", `Welcome ${fname} ${lname}`),
        customerProps.important && e("p", {className:"important"}, "We're very happy to see you again!"),
        e("div", {className:"suggestion-list"},
            e("p", "Please consider buying the following stuff!"),
            e("ul", customerProps.suggestions.map(item => e("li", item)))
        )
    )
)

The protected method bdElements returns the Element tree that describes the structure, properties, and connections of the DOM tree prescribed by a particular Component subclass. An override for bdElements is almost-always provided in subclass definitions.

The method render transforms the declaration into an actual DOM tree and saves a reference to the root at instance.bdDom.root (see bdDom). The method unrender destroys the DOM tree and any resources consequent rendering.

Life cycle methods like render and unrender are rarely applied explicitly; this is discussed in the section Typical Life Cycles, later.

Life Cycle

Component instances have the following life cycle states:

  1. Unrendered: the component's DOM tree has not been instantiated. rendered and attachedToDoc are false. The component's public API is fully functional according to the semantics of that API. For example, addClassName and removeClassName can be applied to an unrendered component, and when that component is finally rendered, the className property of bdDom.root (see bdDom) will be set accordingly. Of course some public functions are nonsensical when applied to an unrendered component. For example, focus simply results in a no-op when applied to an unrendered component.
  2. Rendered: the component's DOM tree has been instantiated, but not attached to the document. rendered is true, but attachedToDoc is false.
  3. Attached: the component's DOM tree has been instantiated and is attached to the document body. rendered and attachedToDoc are both true.
  4. Destroyed: destroy has been applied to the component. The component's DOM tree has been destroyed (if it was ever rendered) and all resources (handles to watchers and events, references to dom nodes, etc.) have been destroyed. Once destroyed, the component instance is in an irreversible dead state that should be easily marked for garbage collection by the JavaScript runtime environment.

Upon creating an component ("newing" up a component type), the component is in the unrendered state. A component may transition to/from the rendered and/or attached states any number of times by applying life cycle methods with the exception of destroy (see above).

Remember, life cycle methods like render and unrender are rarely applied explicitly; this is discussed in the section Typical Life Cycles, later.

Applying destroy to a Component instance is not strictly required to ensure garbage collection of an instance that is no longer in use so long as all references to that instance have been deleted. The most common places to miss references to a Component instance are found in event handler and watcher connections that reference the component. So long as handlers and watchers are connected through eventHub.advise and watchHub.watch, all connections will be automatically destroyed by destroy. This is the raison d'ĂȘtre for destroy.

Child Managment

Component instances may contain other Component instances. These other instances are termed "children" and an instance that contains a child is termed the "parent" of that child. insChild and delChild allow children to be explicitly inserted/deleted to/from the children collection of a parent. While these methods are useful for some Component subclasses that implement collections like lists and grids, usually children are created, rendered, and inserted automatically when a parent is rendered as a consequence of the parent's Element tree containing Component nodes. Recall the rename element tree we saw earlier; here it is in a more-complete context. It contains four children--two Labeled instances and two Button instances.

class RenameDialog extends Dialog {
    bdElements(){
        return e("div", {className: "renameDialog"},
            e("div",
                e(Labeled, {label:"Current Name", value: this.currentName}),
                e(Labeled, {label:"New Name", value: e(Input, {size:50, bdReflect:"name"})})
            ),
            e("div", {className: "bottomButtons"},
                e(Button, {label: "Cancel"}),
                e(Button, {label: "OK"})
            )
        );
    }

    // other stuff...
}

// such a dialog may be used like this...

Dialog.show(RenameDialog, {currentName: theCurrentName}).then(
    result => {
      if(result){
          // result is the new name...rename whatever
      }//else the dialog was canceled
    }
);

Notice how bdElements in the example above describes a DOM tree and a set of Component instances used to "decorate" that tree. All children mentioned in the Element tree given by bdElements are automatically instantiated and rendered when the parent component is rendered. Naturally, each child instance provides its own DOM tree and decorations and so on until the component instance is completely rendered. This is a good example of a Component subclass that is both a specialization of another Component subclass, namely Dialog, and a composition of other Component subclasses, namely Labeled and Button.

When children instances are created implicitly during rendering as given by bdElements, the root of a particular child's DOM tree is appended to the parent DOM node as given by the Element tree; this is obvious from the example above. When a child is explicitly inserted with insChild, the child may be appended to the parents DOM tree at several different locations. See insChild for details.

Children may exist only when the parent is rendered. All children of a particular parent are automatically destroyed when that parent is unrendered. If this default behavior is not appropriate for a particular subclass design, an override to unrender can be provided; for example:

unrender(){
    this.cachedChildren = this.children.map(child => this.delChild(child, true));
    super.unrender();
}

By applying delChild with a preserve argument of true, the child will be preserved (not destroyed) upon removing it from the children collection (see delChild). Presumably, cachedChildren would be used in some way by other parts of the implementation--almost certainly during render. Caching designs can be used to dramatically improve performance for certain kinds of components.

insChild and delChild are most often used in Component subclasses that present collections, for example lists and grids. Since collections ofter require some kind of re-ordering functionality, Component provides reorderChildren, which allows the children of a parent to be reordered in-place. This machinery is the most performant design theoretically possible; hand-tuned designs will be no faster.

Owing to the design of Component's child management machinery, rendering and unrendering a particular Component instance automatically creates, renders, unrenders, and destroys that instance's children as the instance lives its lifetime. Therefore constructing an application is a matter of explicitly rendering a top-level component and then allowing that component to insert/remove children as required given user stimulation and that component's semantics. Each parent will manage its own children semantics similarly. Indeed, many kinds of applications include a single, explicit render of the top-level component instance.

Typical Life Cycles

The following examples demonstrate the typical methods used to manage life cycles. In the examples Panel is a subclass of Component that implements a home automation dimmer panel interface; panel is an instance of Panel. Similarly, TopFrame is a subclass that implements the top container of a home automation application and top is an instance of TopFrame.

  1. An application creates, renders, and attaches a single Component instance with the render function:

    let top = render(e(TopFrame), "root");
  2. Children are created, rendered, and attached to existing Components with insChild:

    let panel = top.insChild(e(Panel, {id:"bathroom"}));
  3. Children are detached, unrendered, and destroyed with delChild:

    top.delChild(panel);
    // panel is dead forever
  4. Children can be detached and unrendered, but not destroyed by providing true for the preserve argument of delChild.

    top.delChild(panel, true);
    // panel can be rendered/attached again at some time in the future
  5. Usually, children are inserted consequent to some event caused by the user:

    globalEventGenerator.advise(
      "onPanelDemand", (panelId) => top.insChild(e(Panel, {id:panelId})));
  6. Putting this all together, the "onPanelDemand" event handler may cache panels, perhaps, something like this:

    let panelCache = {};
    let currentPanel = 0;
    globalEventGenerator.advise("onPanelDemand", (panelId) => {
        top.delChild(currentPanel, true);
        currentPanel =
            panelCache[panelId] =
                top.insChild(panelCache[panelId] || e(Panel, {id:panelId}));
    });

Most applications use the methods demonstrated above most of the time. However, there are many additional methods to create, render, unrender, attach, detach, and destroy Component instances. See constructor, render, unrender, insChild, and delChild for further details.

Property Managment and Protected Names

Component inherits the mixin class watchHub, which provides an interface to register property watchers and fire mutation notifications to those watchers. Here's how this works: For each watchable public property name, a Component subclass defines an associated protected name that is unlikely to clash with any potential public name--public names that may be desirable not only in the subclass being defined, but also in any potential subclasses of the subclass. All Backdraft-defined protected names begin with the prefix "bd", for example, "bdElements", "bdClassName", "bdHasFocus", and so on. The actual, protected property value is stored at the protected name. Then a getter, and, if the property is mutable, a setter are provided to give public access to the protected property. Finally, watchHub.bdMutate is used to detect and signal mutations. For example, here is how the public property "foo" could be implemented in an imaginary Backdraft component.

class SomeComponent extends Component{
    set foo(){
        this.bdMutate("foo", "bdFoo", value);
    }
    get foo(){
        return this.bdFoo;
    }
}

let c = new MyComponent();
c.watch("foo", (newValue, oldValue) => console.log("***", newValue, oldValue));

c.foo = "bar" // => *** "bar" undefined
c.foo = "baz" // => *** "baz" "bar"
c.foo = "baz" // "baz"==="baz" => the watcher is not applied

The example uses "bd" to prefix "foo" when defining the protected property address; this prefix is reserved by the Backdraft library and client code should choose another prefix. Prefixes should be selected so they are likely universally unique.

Either or both of the public and private property name can be symbols

In this pattern, we say...

  1. bdFoo is a protected property
  2. foo is a public property
  3. foo reflects bdFoo
  4. bdFoo is reflected by foo

Noice that, but not supplying a getter, a public property could be made read-only while the protected property remains mutable. This is a common pattern where a public property reflects an internal state, but that internal state is only allowed to be changed by some internal process. This idea can be taken a step further where the protected property doesn't exist at all and the read-only public property is a reflection of some internal state calculation.

Here is a list of the public properties defined by Component together with the protected properties/state, they reflect.

id - > read-only property defined at construction
rendered - > !!bdDom, bdDom exists iff the component has been rendered
attachedToDoc - > bdAttachedToDoc
className - > bdClassName
hasFocus - > bdHasFocus, automatically set by focusManager
enabled - > !bdDisabled
disabled - > bdDisabled
tabIndex - > bdTabIndex
visible - > reflects/mutates "bd-hidden" in className
title - > bdTitle

Event Managment

Component inherits the mixin class eventHub, which provides an interface to register event handlers and fire event notifications to registered handlers. Component does not define any events itself. See eventHub.advise and eventHub for further details.

Element [class]

describes the DOM of a Component

Description

Element instances are used to describe the DOM tree defined by a Component subclass as returned by Component.bdElements. Each Element instance represents a node in a tree that describes either a DOM node or a Component instance; further, each instance can contain children, allowing a tree structure to be defined.

Elements are immutable and encapsulate four things:

type

either a native DOM node tag or a subclass of Component

ctorProps

a Hash of (property -> value) that is used to initialize the node when it is created

ppFuncs

a Hash of (post-processing-function -> arguments) that gives a list of functions that are executed immediately after the node is created and initialized

children

a list of children Element instances

Typically, Element instances are created with the factory functions e or svg.

WatchableRef [class]

provides for retrieving and watching a formatted property value within a watchable object

Description

WatchableRef encapsulates two operations on a particular property within watchHub or Watchable instance:

  1. retrieving and formatting the current value of the property
  2. connecting watchers to the property, ensuring new/old values applied to the watchers are formatted and the watchers are called only if the formatted new/old values mutate

The property of interest in the watchable object is termed the "reference property" and the owning object is termed the "reference object". Both the reference property and reference object are initialized at construction and are immutable for the lifetime of a WatchableRef instance.

The example below sets up a WatchableRef on the property a of data. Further a formatter is provided that converts the number to either the string "odd" or "even" depending upon its value.

let data = toWatchable({a:1, b:2});
let ref = new WatchableRef(data, "a", value => value % 2 ? "odd" : "even");

// ref.value gives the current value of the reference property in the
// reference object applied to the formatter
console.log(ref.value); // => odd
data.a = 2;
console.log(ref.value); // => even

// set up a watch on the ref...
ref.watch((newValue, oldValue, refObj, prop) => {
    console.log(`new=${newValue}, old=${oldValue}, prop=${prop}, ${refObj===data}`);
});

data.a = 3; // => new=odd, old=even, prop=a, true
data.a = 5; // since the formatted value did not change, the watcher is not called
            // note: the formatter was called
data.a = 6  // => new=even, old=odd, prop=a, true

WatchableRefs may be set up to reference all properties on a reference object by omitting the reference property at construction. Refs set up in this manner are termed "star" refs. For star refs, the value property returns the result of the formatter applied to the reference object. The arguments provided to watchers connected to star refs are also different as follows:

  1. newValue is the result of the formatter applied to the current value of the reference object
  2. if the mutated property is a non-scalar type, then oldValue is the constant UNKNOWN_OLD_VALUE
  3. prop is an array that lists the property path to the particular property in the reference object that mutated

For example:

let data = toWatchable([{a:1, b:2}, {c:3, d:4}]);
let ref = new WatchableRef(data, value => {
    console.log("in formatter", value===data);
    return JSON.stringify(data);
});

// ref.value returns the formatted reference object
console.log(ref.value);
    // => in formatter true
    // => [{"a":1,"b":2},{"c":3,"d":4}]
data[0].b= 3.14;
console.log(ref.value);
    // => in formatter true
    // => [{"a":1,"b":3.14},{"c":3,"d":4}]


ref.watch((newValue, oldValue, referenceObject, prop) => {
    console.log("newValue: ", newValue);
    console.log(oldValue===WatchableRef.UNKNOWN_OLD_VALUE);
    console.log(referenceObject===data);
    console.log(prop);
});

data[1].c = "test";
    // => in formatter true
    // => newValue:  [{"a":1,"b":3.14},{"c":"test","d":4}]
    // => true
    // => true
    // => (2) ["1", "c"]

The formatter is optional; if missing, the formatter defaults to the identity function; see constructor.

WatchableRefs are an advanced featured. WatchableRefs are used internally by Backdraft to set up reflectors.

biBind [function]

makes connections so that mutations in one property are immediately propagated to another property

Synopsis

(src, srcProp, dest, destProp) => handles[]
src
watchHub | Watchable
the source object to watch
srcProp
string | symbol
the property in src to watch
dest
watchHub | Watchable
the source object to watch
destProp
string | symbol
the property in src to watch
handles
Destroyable[]
Destroyable objects that can be used to terminate the binding

Description

Initializes src[srcProp] to dest[destProp], then sets up watchers on both properties so that any mutation on one is propagated to the other. Really just syntax sugar:

biBind(src, srcProp, dest, destProp)

is equivalent to

src[srcProp] = dest[destProp];
return [bind(src, srcProp, dest, destProp), bind(dest, destProp, src, srcProp)];

bind [function]

makes a connection so that mutations in one property are immediately propagated to another property

Synopsis

(src, srcProp, dest, destProp) => handle
src
watchHub | Watchable
the source object to watch
srcProp
string | symbol
the property in src to watch
dest
Object
the destination object
destProp
string | symbol
the property in dest to mutate
handle
Destroyable
Destroyable object that can be used to terminate the binding

Description

Initializes dest[destProp] to src[srcProp], then sets up a watcher on src[srcProp] to propagate any mutations to dest[destProp]. Really just syntax sugar:

bind(src, srcProp, dest, destProp)

is equivalent to

dest[destProp] = src[srcProp];
if(src.isBdWatchHub){
    return src.watch(srcProp, newValue => dest[destProp] = newValue);
}else if(isWatchable(src)){
    return watch(srcProp, newValue => dest[destProp] = newValue);
}else{
    throw new Error("src is not watchable");
}

connect [function]

connect a listener function to an event signaled by an object

Synopsis

(eventType, eventSourceType1, handler, useCapture) => handle
(eventType, eventSourceType2, handler) => handle
eventType
string | symbol
the event to connect
eventSourceType1
object that contains addEventListener method
the source of the event
eventSourceType2
object that contains advise method
the source of the event
handler
(eventObject) => any
the event handler
handle
Destroyable
Destroyable object that can be used to terminate the advise

Description

Connects listener to the event eventType generated by eventSourceType1/eventSourceType2 and returns a Destroyable that destroys the connection. eventSourceType1 source objects (typically DOM nodes) are connected with addEventListener; eventSourceType2 source objects are connected with advise with semantics as given by eventHub.advise. Any target that implements addEventListener and removeEventListener works with connect.

useCapture has semantics as given by eventSourceType1.addEventListener.

If the document supports touch events, then event types mousedown, mousemove, and mouseup result in connecting listener to touchstart, touchmove, and touchend, respectively in addition to requested event type. In this case, the returned Destroyable will destroy both connections.

create [function]

create a DOM node

Synopsis

(htmlTag, props) => domNode
(namespacedTag, props = {}) => domNode
htmlTag
string
an HTML tag name
namespacedTag
[namespace, tag]
namespace (a string) is a www.w3.org namespace;
tag (a string) gives a element tag within that namespace
props
Hash
optional, default = {}
hash of (attributeName -> value) pairs to initialize new DOM node
domNode
DOM node
the new DOM node

Description

If htmlTag is given, then creates a new DOM node with document.createElement; if namespacedTag is given, then creates a new DOM node with document.createElementNS. After the node is created, attributes values are initialized by applying setAttr(node, hash).

destroyDomChildren [function]

destroys all children of a DOM node

Synopsis

(node) => void
node
DOM node
the parent DOM node of which children are to be destroyed

Description

Removes all children of node.

destroyDomNode [function]

destroys a DOM node

Synopsis

(node) => void
node
DOM node
the DOM node to destroy

Description

Removes the node from its parent.

e [function]

Element factory

Synopsis

(htmlTag, props = {}, ...children) => element
(namespacedTag, props = {}, ...children) => element
(component, prop s= {}, ...children) => element
htmlTag
string
an HTML tag name
namespacedTag
[namespace, tag]
namespace (a string) is a www.w3.org namespace;
tag (a string) gives a element tag within that namespace
component
subclass of Component constructor
component type
props
Hash
optional, default = {}
The Hash of (property -> value) and (post-processing-function -> arguments) pairs that describe how to initialize Element.ctorProps and Element.ppFuncs.
children
falsey | Element | children[]
optional
children can be falsey | Element | children[], thereby allowing arbitrarily nested arrays of children.
element
Element
the new Element

Description

Syntax sugar:

e(arguments)

is equivalent to

new Element(arguments)

svg provides syntax sugar for SVG elements.

eql [function]

tests if two values are equal

Synopsis

(refValue, otherValue) => boolean
refValue
any
one side of the comparison; used to find an eql comparator
otherValue
any
one other side of the comparison

Description

Compares two values according to the semantics of the comparator located at eqlComparators.get(refValue.constructor), if any; otherwise the comparison uses ===. Used by all Backdraft watch machinery to determine if a potential mutation is an actual mutation. Here is the complete code for eql:

function eql(refValue, otherValue){
    if(!refValue){
        return otherValue === refValue;
    }else{
        let comparator = eqlComparators.get(refValue.constructor);
        if(comparator){
            return comparator(refValue, otherValue);
        }else{
            return refValue === refValue;
        }
    }
}

Client code can add comparators for client-defined types. For example:

class Point {
    constructor(x, y){
        this.x = x;
        this.y = y;
    }
}

eqlComparators.set(Point, (lhs, rhs) => {
    return lhs instanceof Point && rhs instanceof Point && lhs.x===rhs.x && lhs.y===rhs.y;
});

let p1 = new Point(1, 2);
let p2 = new Point(1, 2);
let p3 = new Point(3, 4);
eql(p1, p2);         // true;
eql(p1, p3);         // false;
eql(p1, {x:1, y:2}); // false;

getAttr [function]

get DOM node attribute value

Synopsis

(node, attributeName) => string
(id, attributeName) => string
(component, attributeName) => string
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
attributeName
string
name of the attribute whose value you want to get

Description

Returns the value of a specified attribute on the source DOM node. The first signature is the only substantive signature, the remaining signatures are syntax sugar:

getAttr(component, attributeName)
getAttr("someId", attributeName)

are equivalent to:

getAttr(component.bdDom.root, attributeName)
getAttr(document.getElementById("someId"), attributeName)

getComputedStyle [function]

get window.getComputedStyle for a DOM node

Synopsis

(node) => CSSStyleDeclaration
(id) => CSSStyleDeclaration
(component) => CSSStyleDeclaration
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
CSSStyleDeclaration
CSSStyleDeclaration
live CSSStyleDeclaration

Description

Returns a live CSSStyleDeclaration object via window.getComputedStyle.

The first signature is the only substantive signature, the remaining signatures are syntax sugar:

getComputedStyle(component)
getComputedStyle("someId")

are equivalent to:

getComputedStyle(component.bdDom.root)
getComputedStyle(document.getElementById("someId"))

getPosit [function]

get the size and position of a DOM node relative to the viewport

Synopsis

(node) => Posit
(component) => Posit
(id) => Posit
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root

Description

Returns a Posit object as computed by node.getBoundingClientRect. The maxH, maxW, and z properties of the returned Posit object are not defined.

The first signature is the only substantive signature, the remaining signatures are syntax sugar:

getPosit(component)
getPosit("someId")

are equivalent to:

getPosit(component.bdDom.root)
getPosit(document.getElementById("someId"))

getPostProcessingFunction [function]

return a post-processing function from the post-processing catalog

Synopsis

(ppfProcId) => (ppfOwner, ppfTarget, ...args) => void
ppfProcId
string | symbol
target post-processing function identifier

Description

Returns the requested post-processing function if it exists in Backdraft's private catalog of post-processing functions; undefined otherwise. See post-processing-functions for definitions of ppfProcId, ppfOwner, and ppfTarget and other details.

Function are inserted into the catalog with insPostProcessingFunction and replacePostProcessingFunction.

getStyle [function]

get DOM node style value

Synopsis

(node, styleName) => string
(id, styleName) => string
(component, styleName) => string
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
styleName
string
name of the style whose value you want to get

Description

Returns the value of a specified style on the source DOM node. If the style is a pixel value, then it is returned as a number.

The first signature is the only substantive signature, the remaining signatures are syntax sugar:

getStyle(component, styleName)
getStyle("someId", styleName)

are equivalent to:

getStyle(component.bdDom.root, styleName)
getStyle(document.getElementById("someId"), styleName)

getStyles [function]

get multiple style values

Synopsis

(node, ...styleNames) => string
(id, ...styleNames) => string
(component, ...styleNames) => string
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
styleNames
string | Hash | string[]
the list of styles to query

Description

Processes ...styleNames to get a list of style names:

  • strings then are added to the list
  • string[]s are concatenated to the list
  • Object.getOwnPropertyNames(hash) are concatenated to the list

Then, given the list of style names, list, returns result which is computed as follows:

result = {};
list.forEach(name => result[name] = getStyle(node, name));

The first signature is the only substantive signature, the remaining signatures are syntax sugar:

getStyles(component, ...styleNames)
getStyles("someId", ...styleNames)

are equivalent to:

getStyles(component.bdDom.root, ...styleNames)
getStyles(document.getElementById("someId"), ...styleNames)

getWatchableRef [function]

WatchableRef factory

Synopsis

(referenceObject, referenceProp = WatchableRef.STAR, formatter = x => x)
referenceObject
watchHub | Watchable
the reference watchable object
referenceProp
string | symbol
optional, default = STAR
the property within referenceObject to reference
formatter
function(any) => any
optional, default = x => x
applied to the reference property value to compute WatchableRef.value and applied to newValue/oldValue args before applying watchers

Description

Syntax sugar:

getWatchableRef(args)

is equivalent to

new WatchableRef(args

insPostProcessingFunction [function]

insert a post-processing function into Backdraft's private catalog

Synopsis

(ppfProcId, ppf) => void
ppfProcId
string | symbol
the identifier of the post-processing function
ppf
function(ppfOwner, ppfTarget, function-dependent-parameters)
the post-processing function

Description

Inserts ppf at ppfProcId into Backdraft's private catalog of post-processing functions. An exception is thrown if a function already exists for ppfProcId. See replacePostProcessingFunction to replace an already-existing function. See post-processing-functions for definitions of ppfProcId, ppfOwner, and ppfTarget and other details.

insert [function]

insert a DOM node with respect to another DOM node

Synopsis

(node, refNode, position = "last") => node | nodes[] | void
node
DOM node
DOM node to insert
refNode
DOM node
reference DOM node
position
Position
location to insert node with respect to reference node
node
DOM node
refNode when position==="replace"
nodes
array of DOM node
children of refNode when position==="only"

Description

Inserts node with respect to refNode as given by position:

"first" insert node as the first child of refNode
"last" insert node as the last child of refNode
"only" insert node as the only child of refNode; all existing children are removed and returned.
"replace" remove refNode from its parent and replace it with node; refNode is returned
"before" insert node as a sibling of refNode, before refNode
"after" insert node as a sibling of refNode, after refNode

isWatchable [function]

predicate that says if source is a Watchable

Synopsis

(source) => boolean
source
any
value to test

Description

Returns true is source is a watchHub or a Watchable; false otherwise.

render [function]

render a component and optionally insert the rendered DOM relative to another DOM node

Synopsis

(componentClass, kwargs, node, position) => component
(componentClass, kwargs, id, position) => component
(componentInstance, node, position) => component
(componentInstance, id, position) => component
(element) => component
(element, node, position) => component
(element, id, position) => component
componentClass
subclass of/or Component constructor
the component class to create
componentInstance
instance of Component
the component instance to append to the document
element
Element
describes the component to create
node
DOM node
optional
the reference node to which to insert the component's rendered DOM
id
string
optional
equivalent to providing node===document.getElementById(id)
position
Position
optional, default = last
position relative to node to insert the component's rendered DOM

Description

Optionally creates a new component instance, unconditionally renders that instance, and optionally inserts the rendered DOM tree with respect to node.

The most common usage of render is with the signature (componentClass, kwargs, id), which instantiates a new componentClass instance, renders that instance, and appends the root of the DOM rendering to document.getElementById(id). In code:

import SomeComponentClass from "./SomeComponentClass.js";

let kwargs = {/* as required */};
render(SomeComponentClass, kwargs, "root");

// is equivalent to...

let instance = new SomeComponentClass(kwargs);
instance.render();
insert(instance.bdDom.root, document.getElementById("root"), "last");

insert is the Backdraft insert function. Since position was not given in the example, it defaulted to "last". In general, the position argument controls where the rendered DOM is inserted as described by insert.

If the node argument is missing, the rendered DOM is not inserted anywhere. This applies for all signatures.

The second most common usage of render is to render and insert an already-created component instance. In code:

import SomeComponentClass from "./SomeComponentClass.js";

let instance = new SomeComponentClass(kwargs);

// at some point later...

render(instance, "root");

// is equivalent to...

instance.render();
insert(instance.bdDom.root, document.getElementById("root"), "last");

Applying render to an Element instance is unusual, but is provided for completeness. If element.type references a tag name (see Element.type, then render creates the component new Component({element:element}); otherwise render creates the component new element.type(element).

The component instance is returned by all signatures.

replacePostProcessingFunction [function]

insert/replace a post-processing function into Backdraft's private catalog

Synopsis

(ppfProcId, ppf) => void
ppfProcId
string | symbol
the identifier of the post-processing function
ppf
function(ppfOwner, ppfTarget, function-dependent-parameters)
the post-processing function

Description

Inserts ppf at ppfProcId into Backdraft's private catalog of post-processing functions. If a function already exists for ppfProcId, then that function is replaced with ppf. See post-processing-functions for definitions of ppfProcId, ppfOwner, and ppfTarget and other details.

setAttr [function]

set DOM node attribute value

Synopsis

(node, attributeName, value) => void
(component, attributeName, value) => void
(id, attributeName, value) => void
(node, hash) => void
(component, hash) => void
(id, hash) => void
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
attributeName
string
name of the attribute whose value you want to set
value
string
value to assign to attribute
hash
Hash (attributeName -> value)
list of (attributeName -> value) pairs to set

Description

Set the value of a specified attribute on the source DOM node. The first signature is the only substantive signature, the remaining signatures are syntax sugar:

setAttr(component, attributeName, value)
setAttr("someId", attributeName, value)
setAttr(node, hash)
setAttr(component, hash)
setAttr("someId", hash)

are equivalent to:

setAttr(component.bdDom.root, attributeName, value)
setAttr(document.getElementById("someId"), attributeName, value)
Object.getOwnPropertyNames(hash).forEach(p => setAttr(node, p, hash[p])
Object.getOwnPropertyNames(hash).forEach(p => setAttr(component.bdDom.root, p, hash[p])
Object.getOwnPropertyNames(hash).forEach(p => setAttr(document.getElementById("someId"), p, hash[p])

setPosit [function]

set the size and/or position of a DOM node

Synopsis

(node, posit) => void
(component, posit) => void
(id, posit) => void
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
posit
Posit
position properties to set

Description

Sets the DOM style size and/or position values in pixels as given by posit. posit need not provide all possible values. Any property in posit that is not a property defined by Posit is ignored.

The first signature is the only substantive signature, the remaining signatures are syntax sugar:

setPosit(component, posit)
setPosit("someId", posit)

are equivalent to:

setPosit(component.bdDom.root, posit)
setPosit(document.getElementById("someId"), posit)

setStyle [function]

set DOM node style value

Synopsis

(node, styleName, value) => void
(component, styleName, value) => void
(id, styleName, value) => void
(node, hash) => void
(component, hash) => void
(id, hash) => void
node
DOM node
source DOM node
id
string
source DOM node is given by document.getElementById(id)
component
Component
source DOM node is given by component.bdDom.root
styleName
string
name of the style to set
value
string
value to assign to style
hash
Hash (styleName -> value)
list of (styleName -> value) pairs to set

Description

Set the value of a specified style on the source DOM node. The first signature is the only substantive signature, the remaining signatures are syntax sugar:

setStyle(component, styleName, value)
setStyle("someId", styleName, value)
setStyle(node, hash)
setStyle(component, hash)
setStyle("someId", hash)

are equivalent to:

setStyle(component.bdDom.root, styleName, value)
setStyle(document.getElementById("someId"), styleName, value)
Object.getOwnPropertyNames(hash).forEach(p => setStyle(node, p, hash[p])
Object.getOwnPropertyNames(hash).forEach(p => setStyle(component.bdDom.root, p, hash[p])
Object.getOwnPropertyNames(hash).forEach(p => setStyle(document.getElementById("someId"), p, hash[p])

stopEvent [function]

prevent default event processing and stop even propagation

Synopsis

(eventObject) => void
eventObject
Object
optional, default = undefined
the object applied to an event listener

Description

Syntax sugar:

stopEvent(event)

is equivalent to

if(event && event.preventDefault){
    event.preventDefault();
    event.stopPropagation();
}

svg [function]

specialized Element factory

Synopsis

(svgTag = "svg", props = {}, ...children)
svgTag
string
optional, default = "svg"
a SVG tag name
props
Hash
optional, default = {}
The Hash of (property -> value) and (post-processing-function -> arguments) pairs that describe how to initialize Element.ctorProps and Element.ppFuncs.
children
falsey | Element | children []
optional
children can be falsey | Element | [children], thereby allowing arbitrarily nested arrays of children.

Description

Syntax sugar:

svg(svgTag, arguments)

is equivalent to

new Element(["http://www.w3.org/2000/svg", svgTag], arguments)

toWatchable [function]

transforms a JavaScript Object so that mutations to any property can be observed with a watcher

Synopsis

(source) => Watchable
source
Object
Any Javascript Object, including arrays.

Description

toWatchable transforms any JavaScript Object, including arrays, so that property mutations throughout the object's property hierarchy (not prototype chain) can be observed by connecting a watcher with watch or WatchableRef. Mutations detected include inserting and deleting properties as well as mutating property values. Any inserted properties that are themselves JavaScript Objects, including arrays, are inserted by value (not reference) and the value is converted to a watchable before insertion.

The conversion of the target object is both in-place and deep:

  • Each property of the target object that has an object value, including arrays, is converted to a Watchable
  • The entire object hierarchy is replaced

Typically, source objects must be pure Objects or Arrays (that is, not subclasses of Object or Array). As the property hierarchy is traversed during transformation, any property value that is not a pure Object or Array is treated as a scalar value and its contents are not watchable. For example, consider the following object:

class DoB extends Data {
    get day() { return this.getDate(); }
    get month() { return this.getMonth() + 1; }
    get year() { return this.getFullYear(); }
}

{
    name: {
        fname: "John",
        lname: "Doe"
    },
    dob: new DoB(1960, 5, 7)
}

name, name.fname, name.lname, and dob are all watable. However, dob.day, dob.month, and dob.year are not watchable.

See watch and Watchable Data for examples.

watch [function]

connect a watcher to a watchable

Synopsis

(watchable, prop = STAR, watcher) => handle
(watchable, props, watcher) => handles[]
(watchable, hash) => handles[]
watchable
Watchable
the object to watch
prop
string | symbol
optional, default = STAR
the property to watch
watcher
Watcher
watcher to apply upon mutation of prop
handle
Destroyable
Destroyable object that can be used to terminate the watch
handles
Destroyable
array of Destroyable object that can be used to terminate the watches

Description

The first signature is the only substantive signature, the remaining signatures are syntax sugar discussed at the end.

When watch is applied with a prop argument other than STAR and the watcher is not being signaled consequent to a mutation bubbling up (see below) then watcher will be applied to the following arguments:

  • newValue: the value of watchable[prop] after mutation
  • oldValue: the value of watchable[prop] before mutation
  • prop: prop provided when watch was applied to connect the watcher
  • target: the just-mutated watchable provided when watch was applied to connect the watcher

Otherwise, the watchers will be applied to the following arguments:

  • newValue: same as target
  • oldValue: UNKNOWN_OLD_VALUE
  • prop: an array that lists the property path to the particular watchable property that mutated
  • target: the just-mutated watchable provided when watch was applied to connect the watcher

All signatures return a handle or handles to all watches that were connected.

Example:

let target = toWatchable([{fname: "John", lname: "Doe"}]);

// connect to fname
let h1 = watch(target[0], "fname", (newValue, oldValue, target, prop) => {
    console.log(
        "target[0].fname watcher:",
        "newValue=", JSON.stringify(newValue),
        "| oldValue=", JSON.stringify(oldValue),
        "| target=", JSON.stringify(target),
        "| property=", prop);
});


// connect to the first item in tager
let h2 = watch(target, 0, (newValue, oldValue, target, prop) => {
    console.log(
        "target[0] watcher:",
        "newValue=", JSON.stringify(newValue),
        "| oldValue=", JSON.stringify(oldValue),
        "| target=", JSON.stringify(target),
        "| property=", prop);
});

// connect to the entire target
let h3 = watch(target, (newValue, oldValue, target, prop) => {
    console.log(
        "target watcher:",
        "newValue=", JSON.stringify(newValue),
        "| oldValue=", JSON.stringify(oldValue),
        "| target=", JSON.stringify(target),
        "| property=", prop);
});

target[0].fname = "Joe";
// target[0].fname watcher: newValue= "Joe" | oldValue= "John" | target= {"fname":"Joe","lname":"Doe"} | property= ["fname"]
// target[0] watcher: newValue= {"fname":"Joe","lname":"Doe"} | oldValue= {"value":"UNKNOWN_OLD_VALUE"} | target= [{"fname":"Joe","lname":"Doe"}] | property= (2) ["0", "fname"]
// target watcher: newValue= [{"fname":"Joe","lname":"Doe"}] | oldValue= {"value":"UNKNOWN_OLD_VALUE"} | target= [{"fname":"Joe","lname":"Doe"}] | property= (2) ["0", "fname"]


target[0].lname = "Smith";
// target[0] watcher: newValue= {"fname":"Joe","lname":"Smith"} | oldValue= {"value":"UNKNOWN_OLD_VALUE"} | target= [{"fname":"Joe","lname":"Smith"}] | property= (2) ["0", "lname"]
// target watcher: newValue= [{"fname":"Joe","lname":"Smith"}] | oldValue= {"value":"UNKNOWN_OLD_VALUE"} | target= [{"fname":"Joe","lname":"Smith"}] | property= (2) ["0", "lname"]

h1.destroy();
h2.destroy();

target[0].fname = "Adam";
// since we destroyed the first two watchers, we don't see their output; the third watcher is still connected...
// target watcher: newValue= [{"fname":"Adam","lname":"Smith"}] | oldValue= {"value":"UNKNOWN_OLD_VALUE"} | target= [{"fname":"Adam","lname":"Smith"}] | property= (2) ["0", "fname"]

Notice that when a property within a data hierarchy is mutated, notifications of that mutation bubble up through the hierarchy. For example, mutating data[0].fname signals any watchers connected to data[0].fname, data[0], and data. As notifications are bubbled up, newValues and oldValues provided to the watchers change. For example watchers connected to data[0].fname get new/old values of data[0].fname while watchers connected to data[0] and data get new/old values of data[0] and data respectively. This ensures the type of the new/old value is constant even though the source of the mutation may be different (for example mutating the complete data[0] object compared to just mutating data[0].fname).

In some cases the old value is not provided, but rather a known constant, namely UNKNOWN_OLD_VALUE, is provided. This happens when a contained property is mutated. For example, fname is contained by data[0] and data; similarly, data[0] is contained by data. Backdraft does not supply the old value because it is computationally expensive to provide the value. Think about a data hierarchy that is an array of 10,000 items, each item is an array of 100 items (a 10,000 x 100 grid), and each item is an object with 20 properties. If the old value was provided as mutation signals bubbled up, two complete copies of the 20M item data structure would be required. This is never a problem as the old value is rarely used in watcher functions that answer to bubbled-up signals, and if some particular watcher design requires some aspect of the old value, then such watchers can understand exactly what changed by looking at the prop argument and cache old values as is required by the particular design.

eventHub [mixin]

provides machinery to signal events

Description

eventHub is a mixin class (see mixins) that provides machinery to signal events and further to allow clients to register handlers to receive such signals. The method advise allows clients to register a handler, and the method bdNotify applies all handlers registered to a particular event type. For example:

class SuperClass {
    superclassMethod(){
        console.log("in superclass method");
    }
}
class MyClass extends eventHub(SuperClass){
    stimulate(number){
        if(number % 2){
            this.bdNotify({type: "odd", number: number});
        }else{
            this.bdNotify({type: "even", number: number});
        }
}

let test = new MyClass();

// SuperClass is a superclass of MyClass...
test.superclassMethod(); // => in superclass method

// only print out "odd" events...
test.advise("odd", (e) => console.log(e));

test.stimulate(100); // no output, "even" event was notified
test.stimulate(101); // => {type: "odd", number: 101}

watchHub [mixin]

provides machinery to signal mutations within an object

Description

watchHub is a mixin class (see mixins) that provides machinery to signal mutations within an object and further to allow clients to register watchers to receive such signals. The method watch allows clients to register a Watcher, and the method bdMutateNotify applies all watchers registered to a particular property.

Often watchable properties are implemented by defining a protected property on an object and providing getter/setter proxies on that property. The method bdMutate can be used to signal mutations with this design. See Watchable Properties for details. For example:

class SuperClass {
    superclassMethod(){
        console.log("in superclass method");
    }
}

class MyClass extends watchHub(SuperClass) {
    get myProp(){
        return this._myProp();
    }
    set myProp(value){
        this.bdMutate("myProp", "_myProp", value);
    }
}

let test = new MyClass();

// SuperClass is a superclass of MyClass...
test.superclassMethod(); // => in superclass method"

// connect a watcher...
test.watch("myProp", (newValue, oldValue)=>console.log("new:", newValue, "old:", oldValue));

test.myProp = 3.14;  // new: 3.14 old: undefined
test.myProp = "foo"; // new: foo old: 3.14

withWatchables [mixin]

mixin class that automatically defines watchable properties

Synopsis

withWatchables(superClass = class{}, ...propertyDefs) => class

Description

Defines a new class that has superClass as its base class and contains getters and setters for each property definition in propertyDefs. Each property definition can either give a public-property-name or a (public-property-name, private-property-name) pair. If the first form is given, then that public-property-name must be a string and the private-property-name is automatically calculated to be the public-property-name prefixed by an underscore.

Given a (public-property-name, private-property-name) pair, say ("x", "_x"), withWatchables causes a class with a getter/setter to be defined as demonstrated below.

class MyClass extends withWatchables(Component, ["x", "_x"]){
    // properties and methods for MyClass
}

// is equivalent to...

let temp = class extends Component{
    get x(){
        return this._x;
    }
    set x(value){
        this.bdMutate("x", "_x", value);
    }
}

class MyClass extends temp {
  // properties and methods for MyClass
}

withWatchables is really just sugar to simplify expressing routine class machinery. See also Component.withWatchables for more sugar.

bdAdvise [post-processing-function]

connect an event handler to an event on ppfTarget

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms.

bdAdvise takes a hash from event type, eventType, to handler function, handler. If ppfTarget is a Component, then ppfTarget.advise is applied to (eventType, handler) (see eventHub.advise); otherwise handler is connected by applying connect to (ppfTarget, eventType, handler) and the handle returned is applied to ppfOwner.ownWhileRendered (see Component.ownWhileRendered). For example:

class MyClass extends Component {
    bdElements(){
        return e("div",
            e("div", {bdAdvise: {click: this.onClick.bind(this)}}),
            e(SomeComponent, {bdAdvise: {someEvent: this.onSomeEvent.bind(this)}})
        };
    }
}

The bdAdvise for the freshly-created DIV node, node, causes the following connection:

ppfOwner.ownWhileRendered(connect(node, "click", this.onClick.bind(this)))

And the bdAdvise for freshly-instantiated SomeComponent, instance, causes the following connection:

instance.advise("someEvent" this.onClick.bind(this)))

If a string or symbol is given for the handler, then ppfOwner[handler] is assumed. For example:

e("div", {bdAdvise: {click: "onClick"}})

is equivalent to

e("div", {bdAdvise: {click: this.onClick.bind(this)}})

As described in post-processing-functions, when ppfArgs is a hash, as in the case of bdAdvise, the underscore optimization may be employed. The following is equivalent to DIV connection in the original example:

e("div", {bdAdvise_click: "onClick"})

bdAttach [post-processing-function]

attach a reference to ppfTarget

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms.

When ppfArgs is a string or symbol, prop, bdAttach causes the following processing:

ppfOwner[prop] = ppfTarget;
ppfOwner.ownWhileRendered({
	destroy: function(){
		delete ppfOwner[prop];
	}
});

When ppfArgs is a x => void, func, bdAttach causes the following processing:

func(ppfTarget);

bdChildrenAttachPoint [post-processing-function]

designates ppfTarget as the node to which children are attached

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms.

Designates ppfTarget as the node to which children are attached.

bdReflect [post-processing-function]

reflect a watchable into a property of ppfTarget

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms. Arguments

bdReflect takes a hash from property, termed the targetProperty, in ppfTarget to the arguments (watchable, prop, formatter). watchable, optional, is either a watchHub a Watchable; if missing, watchable defaults to ppfOwner. prop is a string or symbol to watch in watchable. formatter, optional, is a formatter function to apply to watchable[prop] before reflecting into ppfTarget[targetProperty], if missing, formatter defaults to x => x.

To reflect means ppfTarget[targetProperty] is initialized with formatter(watchable[prop]) when ppfTarget is created and further the property is updated to reflect the current formatted value of formatter(watchable[prop]) any time that value mutates. For example, consider the following component:

class MyComponent extends Component.withWatchables("myProp") {
    bdElements(){
        e("div", {bdReflect: {innerHTML: [this, "myProp", v => v ? v : "?"]});
    }
}

The innerHTML of the DIV node will be initialized to this.myProp ? this.myProp : "?" and any time that the value this.myProp ? this.myProp : "?" mutates, innerHTML will be updated accordingly.

The arguments for an particular targetProperty are given as an array; if both watchable and formatter are missing, then arguments may be given as a single scalar prop value. For example:

{bdRefect: {innerHTML: [this, "myProp", v => v ? v : "?"]}}
// arguments: (this, "myProp", v => v ? v : "?")

{bdRefect: {innerHTML: ["myProp"]}}
// arguments: (ppfOwner, "myProp", x => x)

{bdRefect: {innerHTML: "myProp"}}
// arguments: (ppfOwner, "myProp", x => x)

When reflecting a property in the ppfOwner, the watchable argument may be omitted. The following is equivalent to the original example:

bdElements(){
    e("div", {bdReflect: {innerHTML: ["myProp", v => v ? v : "?"]);
}

As described in post-processing-functions, when ppfArgs is a hash, as in the case of bdReflect, the underscore optimization may be employed. The following is equivalent to the original example:

bdElements(){
    e("div", {bdReflect_innerHTML: ["myProp", v => v ? v : "?"]);
}

bdReflect includes an extra expressive optimization by assuming innerHTML if the target property is missing. This allows bdReflect to accept ppfArgs that gives an argument list rather than a hash. The following is equivalent to the original example:

bdElements(){
    e("div", {bdReflect: ["myProp", v => v ? v : "?"]);
}

As noted in bdReflect's signature, the formatter is always optional. So the common case of reflecting a property value in a component into the innerHTML without formatting can be stated quite tersely as follows:

bdElements(){
    e("div", {bdReflect: "myProp");
}

bdReflectClass [post-processing-function]

reflects one or more formatted watchables into ppfOwner.className

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms.

Reflects one or more formatted watchables into ppfOwner.className. ppfArgs is given as a list or arguments (an array). The list is transformed into a set of triples of (watchable, prop, formatter) and each triple causes formatter(watchable[prop]) to be reflected into ppfOwner.className. Each triple is a completely separate reflection. For example:

class MyClass extends Component.withWatchables("error", "value") {
    bdElements(){
        return e("div" {
            bdReflectClass:[
              this, "error", v => v ? "error" : "",
              this, "value", v = v=="" ? "no-value" : ""
            ],
            // other ctorProps and ppFuncs
          },
            // children, if any
        );
    }
}

The two CSS classes, namely "error" and "no-value", will be added/removed from ppfOwner.className depending upon the error and value property values of a particular MyClass instance when that instance is rendered.

Since the prop argument is always required, it is possible to omit optional arguments. The following example gives a bdReflectClass ppfArgs with four triples:

// the following ppfArgs has gives three triples with
bdReflectClass:[
    this, "p1", v => v ? "p1" : "", // all three args
    "p2", "p2", v => v ? "p2" : "", // missing first arg; equivalent to (this, "p1", v => v ? "p1" : "")
    this, "p3",                     // missing last arg; equivalent to (this, "p1", x => x)
    "p4"]                           // missing first and last arg; equivalent to (this, "p1", x => x)

bdTitleNode [post-processing-function]

sets bdDom.titleNode to ppfTarget

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms.

Sets bdDom.titleNode to ppfTarget. See Component.title for details about how a title is reflected.

bdWatch [post-processing-function]

connect a watcher to a watchable property in ppfTarget

Description

This description uses some of the terms ppfOwner, ppfTarget, ppfProcId, ppfProc, and ppfArgs; see post-processing-functions for a definition of these terms.

bdWatch takes a hash from property, watchProp, to watcher function, watcher. ppfTarget must be a Component and bdWatch connects the watcher to the watchProp by applying ppfTarget.watch to (watchProp, watcher) (see watchHub.watch).

bdWatch defines an optimization that allows watcher to be a string or symbol in which case the actual watcher is computed as ppfOwner[watcher].

CSSStyleDeclaration [type]

a CSS declaration block

Description

A CSS declaration block, that exposes style information and various style-related methods and properties. See CSSStyleDeclaration.

Destroyable [type]

An object that provides a means to destroy a resource.

Definition

interface Destroyable {
    destroy: () => void;
}

Description

Provides the method destroy(), which terminates the lifetime of some other object, typically a watch or event handle. destroy() may be called multiple times without harm and applying destroy() on an instance that references an object that has already been destroyed by some other means results in a harmless no-op.

Throughout Backdraft, an object that provides Destroyable is created and returned consequent to connecting some event (e.g., eventHub.advise), watch (e.g., watchHub.watch), or similar type of construct (perhaps in client code), thereby providing a means to terminate the connection. For example:

function doOnceOnClick(node, callback){
    let h = connect(node, "click", (e) => {
        h.destroy();
        callback();
    });
}

Hash [type]

An associative map.

Definition

interface Hash {
    [string]: any
    [symbol]: any
}

Description

A plain JavaScript Object used solely as an associative array where the keys may be strings or symbols and the values may be any type.

Posit [type]

Javascript Object that gives position and size values

Definition

interface Destroyable {
    t?: number;    // top
    b?: number;    // bottom
    l?: number;    // left
    r?: number;    // right
    h?: number;    // height
    w?: number;    // width
    maxH?: number; // maxHeight
    maxW?: number; // maxWidth
    z?: number;    // zIndex
}

Description

A plain JavaScript Object that gives position and size values.

Position [type]

indicates how to insert a node relative to another node

Definition

"first" | "last" | "before" | "after" | "only" | "replace"

Description

A string that indicates how to insert a node relative to another node.

Watchable [type]

JavaScript Object instance (including arrays) that has been transformed by toWatchable

Definition

toWatchable(target)

Description

See toWatchable.

Watcher [type]

callback function signature for watchers

Definition

function(newValue, oldValue, target, prop)

Description

Watcher defines the call signature used by watchHub and Watchable when they notify their connected watchers of a mutation. prop is the property in target that mutated. newValue is the value of the property after the mutation and oldValue is the value before mutation.

STAR [constant]

special value used to register watchers to watch all properties

Definition

const STAR = Symbol("bd-star")

Description

In addition to the Backdraft STAR export, a reference to STAR is located at WatchableRef.STAR

UNKNOWN_OLD_VALUE [constant]

special value supplied for the oldValue parameter when a watcher is applied consequent to nested property mutations

Definition

const UNKNOWN_OLD_VALUE = {value: "UNKNOWN_OLD_VALUE"};

Description

See watch for a detailed description and example.

In addition to the Backdraft UNKNOWN_OLD_VALUE export, a reference to UNKNOWN_OLD_VALUE is located at WatchableRef.UNKNOWN_OLD_VALUE

eqlComparators [variable]

map from constructor functions to eql comparator

Definition

let eqlComparators = new Map()

Description

Used by eql to compute equality. Client code should add entries to the map for intelligent equality calculations.

focusManager [variable]

singleton eventHub that signals focus changes and provides focus state

Definition

let focusManager = new class extends eventHub() {/* ... */}

Description

focusManager is a singleton object that signals focus changes through connectable events and provides focus state values through read-only properties. The following properties are defined:

  • focusedComponent: the component that currently has the focus
  • focusedNode: the DOM node that currently has the focus
  • focusedStack: the stack of components (focusedComponent, parent, grand-parent, etc.) that currently have the focus
  • previousFocusedComponent: the component that had the focus just before focusedComponent gained the focus
  • previousFocusedNode: the DOM node that had the focus just before focusedNode gained the focus

As the focus moves from one component to another, the stack of components from the particular component that holds the DOM node that has the focus to its parent, grand-parent, and so on up through the top-most component changes. This movement of focus can be visualized as popping components off a stack as they lose the focus, and pushing components on a stack as they gain focus; blurComponent and focusComponent signal these pops and pushes respectively. Events are ordered by first signaling all blurred components as they are popped off the stack, then signaling all focused components as the are pushed on the stack, and finally signalling the top-most component with focusedComponent. The following events are defined:

  • blurComponent: a component in the focus stack has lost the focus; the event object applied to handlers provides the component that lost the focus at the property component.
  • focusComponent: a component has been added to the focus stack; the event object applied to handlers provides the component that gained the focus at the property component.
  • focusedComponent: the focusedComponent property changed; the event object applied to handlers provides the component at the property component.

version [constant]

version number of the Backdraft library

Definition

const version = "2.3.1"

Description

The version number of the Backdraft library.

viewportWatcher [variable]

singleton eventHub that signals document scroll and viewport size changes

Definition

let viewportWatcher = new class extends eventHub() {/* ... */}

Description

viewportWatcher is a singleton object that signals document scroll and viewport size changes. The following events are defined:

  • scroll: the document was scrolled
  • resize: the viewport was resized

collection [property]

the data that is associated with each child component

Type

read-write, watchable: array

Description

Each child component is associated with a single item in collection. See insChild for details.

If the array is watchable, then Collection will ensure that its children are inserted/deleted if/when collection changes size.

constructor

instance initializer

Synopsis

new Collection(kwargs)
kwargs
Hash
Collection defines the keywords collection and childType. See Component.constructor for other keywords.

Description

Creates a new Collection instance.

Keyword argument collection initializes the collection property; see collection.

Keyword argument childType gives the constructor (class) for a component type that is used to create children. See insChild.

insChild [method]

creates and inserts a child component into the children collection

Synopsis

instance.insChild(i) => child
i
integer
The index into collection that is associated with the new child.
child
Component
the inserted child

Description

insChild creates a new component of type kwargs.childType. The constructor arguments

{index: i, mix: {collection: this.collection}}

are provided, where this.collection is the value of collection. Children component types should be designed so they synchronize their presentation/semantics with collection[i]. Collection guarantees that a particular child instance is always associated with the same item in collection.

It is the responsibility of the child to watch for mutations on the particular collection item to which it is associated. If the items in collection mutate during the lifetime of a Collection instance, then it is important the collection be set to a Watchable type.

This is a complete override to Component.insChild; signatures and semantics of Component.insChild; are invalid for Collection.

render [method]

creates the component root, then creates one each child for each item in collection

Synopsis

instance.render(callback) => rootDomNode
callback
() => (Destroyable | Destroyable[] | void)
optional
if provided, applied after the instance is rendered. Any Destroyable instances returned are applied to Component.ownWhileRendered.
rootDomNode
DOM node
the root DOM node for the component

Description

If the instance is already in the rendered state, then no-op; otherwise, apply Component.render, then apply insChild for each item in collection.

protected [namespace]

protected properties and methods

Description

These properties and methods should only be used within the implementations of subclasses derived from Collection.

collectionIndex [property]

the index into parent.collection associated with this instance

Type

read-only, watchable: any

Description

Each CollectionChild component is associated with the data given by this.parent.collection[this.collectionIndex].

The owning parent will mutate this value if the CollectionChild instance changes relative position within its siblings without changing the actual data item in collection.

collectionItem [property]

the data item in parent.collection associated with this instance

Type

read-only, watchable: any

Description

Each CollectionChild component is associated with the data given by this.parent.collection[this.collectionIndex].

The owning parent will mutate this value if the underlying collection array is replaced with a different array; the internal implementation of CollectionChild will mutate this value if/when this.parent.collection[this.collectionIndex] is mutated with a different object.

collectionLength [property]

the length of parent.collection

Type

read-only, watchable: any

Description

Each CollectionChild component is associated with the data given by this.parent.collection[this.collectionIndex].

constructor

instance initializer

Synopsis

new CollectionChild(kwargs)
kwargs
Hash
CollectionChild defines the keywords parent and collectionIndex. See Component.constructor for other keywords.

Description

Creates a new CollectionChild instance.

Keyword argument parent initializes the instance's parent. Unlike the normal lifecycle sequence which sets a component's parent after the component is rendered, CollectionChild sets the parent at construction and the parent is immutable. The parent holds the data item to which the particular instance is associated at parent.collection[this.collectionIndex].

Keyword argument collectionIndex gives index into parent.collection of the data item associated with the instance.

protected [namespace]

protected properties and methods

Description

These properties and methods should only be used within the implementations of subclasses derived from CollectionChild.

static [namespace]

static properties and methods defined on CollectionChild

attachedToDoc [property]

indicates whether or not the instance's DOM is a descendant of document.body

Type

read-only, watchable: boolean

Description

true when the instance is rendered and its DOM is a descendant of document.body; otherwise, false.

attachedToDoc reflects bdAttachedToDoc

children [property]

the list of children contained by the instance

Type

read-only, watchable: []Component | undefined

Description

The list of children contained by the instance. Children are only contained when the instance is rendered. If the instance is not rendered or had no children, then children is undefined.

className [property]

the per-instance className

Type

read-write, watchable: string

Description

A sequence of words separated by single spaces that are reflected to bdDom.root.className (see bdDom) when the instance is rendered. The property is maintained and may be mutated whether or not the component is rendered.

When the component is rendered, the concatenation of staticClassName and className is reflected to the DOM root's className attribute, and mutating className causes the DOM root's className attribute to mutate. For example:

class C1 extends Component {
    bdElements(){
      return e("div");
    }
}

// define the static className on the class
C1.className = "base";

let c1 = new C1({});
c1.className;            // ""
c1.className = "foo";
c1.render();
c1.className;            // "foo"
c1.bdDom.root.className; // "base foo"
c1.addClassName("bar");
c1.className;            // "foo bar"
c1.bdDom.root.className; // "base foo bar"

className is initialized during construction and may be mutated at any time directly or through the convenience methods addClassName, removeClassName, toggleClassName. Further, if a className property is provided on the root element returned by bdElements, then the contents of that property is automatically added to the current className value when the component is rendered. Here is an example to illustrate this design.

class C1 extends Component {
    bdElements(){
      return e("div");
    }
}
let c1 = new C1({});
c1.className; // ""
c1.className = "baz";
c1.render();
c1.className; // "baz"

class C2 extends Component {
      bdElements(){
        return e("div", {className:"foo bar"});
      }
}
let c2 = new C2({});
c2.className; // ""
c2.className = "baz";
c1.render();
c1.className; // "foo bar baz"

staticClassName is defined at construction (see constructor) and is immutable during the lifetime of the component; conversely, className is mutable in any life cycle state so long as the component is not destroyed. className does not provide any access--read or write--to staticClassName.

className reflects bdClassName

disabled [property]

indicates the !enabled state

Type

read-only, watchable: boolean

Description

false if the instance is enabled; otherwise true. If false/true, the CSS class "bd-disabled" as automatically added/removed to/from className.

enabled [property]

indicates the enabled state

Type

read-only, watchable: boolean

Description

true if the instance is enabled; otherwise false. If true/false, the CSS class "bd-disabled" as automatically removed/added from/to className.

hasFocus [property]

indicates whether or not the component has the focus

Type

read-only, watchable: boolean

Description

When a DOM node is the document.activeElement (has the browser focus) and that node is contained in the DOM tree of a component, hasFocus is true for that component and all of its ancestors; hasFocus is false otherwise.

hasFocus reflects bdHasFocus; bdHasFocus is mutated by the private methods bdOnFocus and bdOnBlur which are applied by the focusManager.

id [property]

the instance if (if any); otherwise, undefined unique identifier for the Component instance

Type

read-only: string | undefined

Description

A unique identifier for the Component instance; reflected to bdDom.root.id (see bdDom) when the instance is rendered.

parent [property]

the Component instance which holds this instance in its children collection.

Type

read-only, watchable: Component | undefined

Description

The Component instance which holds this instance in its children collection (if any); otherwise, undefined.

rendered [property]

indicates the rendered state

Type

read-only, watchable: boolean

Description

true if the instance is rendered; otherwise false. If rendered is true, bdDom will be defined; if rendered is false, bdDom is undefined.

staticClassName [property]

the static className of the component

Type

read-only: string

Description

Component defines a "static className" at construction which may not be mutated during the lifetime of the component. See constructor for details on initialization. When the component is rendered, the concatenation of staticClassName and className is reflected in the className property of the root node of the component's DOM.

tabIndex [property]

indicates the tabIndex value

Type

read-only, watchable: integer | ""

Description

The tabIndex value. When rendered, this value is reflected into the tabIndex attribute of the tabIndex node given by bdDom.tabIndexNode.; see bdDom.

title [property]

reflected into the title property of the title node

Type

read-write, watchable: string

Description

When a component's Element tree (as given by bdElements) is rendered, one node in the tree is designated the "title node". A reference to this node is maintained at bdDom.titleNode (see bdDom). If not explicitly set by some other means, the bdDom.titleNode is taken as bdDom.root.

visible [property]

indicates the visible state

Type

read-only, watchable: boolean

Description

true if the instance is visible; otherwise false. If true/false, the CSS class "bd-hidden" as automatically removed/added from/to className.

addClassName [method]

adds one or more string components to className

Synopsis

instance.addClassName(...args) => this
arg
string
a component to add
arg
(string | falsey)[]
zero or more components to add
arg
falsey
ignored

Description

Adds all of the string components provided by args to className. Multiples of the same component are only added once. If a component is provided that already exists in the className, then that component is not added again. For example:

let c = new Component({});
c.className = "foo";
c.className;            // => "foo";
c.addClassName("foo");
c.className;            // => "foo";
c.addClassName("bar");
c.className;            // => "foo bar";
c.addClassName("baz",
  false,
  ["this", 0, false, null],
  "bar", "foofoo"
);
c.className;            // => "foo bar this foofoo";

constructor

instance initializer

Synopsis

new Component(kwargs)
kwargs
ConstructorKeywordArgs
Gives a hash of values from which to initialize instance data. ConstructorKeywordArgs lists the properties consumed by Component; all other properties may be defined by subclasses according to their own semantics.

Description

Creates a new Component instance. A reference to kwargs is saved at this.kwargs.

If kwargs.id.toString() is a non-empty string, then the instance property id is defined with the value kwargs.id.toString(); otherwise, the instance property id is set to undefined. Note that id is read-only for the lifetime of the component instance and can never be mutated after construction.

Remaining Component-defined instance properties are initialized as follows:

staticClassName this.constructor.className || ""
className ""
tabIndex ""
title ""
disabled false
enabled true

kwargs may define properties that override the default values described above as follows:

kwargs.staticClassName staticClassName is initialized to kwargs.staticClassName
kwargs.className className is initialized to kwargs.className
kwargs.tabIndex tabIndex is initialized to kwargs.tabIndex
kwargs.title title is initialized to kwargs.title
kwargs.disabled disabled is initialized to kwargs.disabled; enabled is initialized to !kwargs.disabled; if both kwargs.disabled and kwargs.enabled are provided, kwargs.disabled wins.
kwargs.enabled enabled is initialized to kwargs.enabled; disabled is initialized to !kwargs.enabled; if both kwargs.disabled and kwargs.enabled are provided, kwargs.disabled wins.
kwargs.postRender this.postRender is set to kwargs.postRender
kwargs.mix for each key in Reflect.ownKeys(kwargs.mix), this[key] is set to kwargs.mix[key]. This allows per-instance overrides of any method or property and/or the ad hoc addition of per-instance methods/properties at construction.
kwargs.callbacks for each watchableProp in Reflect.ownKeys(kwargs.callbacks) that is also contained in the array this.constructor.watchables, watchHub.watch is applied to (watchableProp, kwargs.callbacks[watchableProp])

for each eventType in Reflect.ownKeys(kwargs.callbacks) that is also contained in the array this.constructor.events, eventHub.advise is applied to (eventType, kwargs.callbacks[eventType])

see watchables and events

Subclasses of Component may override or define addition semantics on kwargs as required by the design of the subclass.

Upon construction, an instance is in the unrendered state.

containsClassName [method]

tests className for the presence of a value

Synopsis

instance.containsClassName(value) => boolean
value
string
the component string to test

Description

this.className must contain the complete component given by value. Consider the following example:

let c = new Component({});
c.className = "foofoo bar baz";
c.containsClassname("foofoo");      // => true
c.containsClassname("foo");         // => false
c.containsClassname("foo bar");     // => false
c.containsClassname("foofoo bar");  // => true
c.containsClassname("bar");         // => true
c.containsClassname("bar baz");     // => true
c.containsClassname("baz bar");     // => false!!

delChild [method]

deletes a child component from the children collection

Synopsis

instance.delChild(child, preserve) => deletedChild
child
Component
the child to delete
preserve
boolean
optional
if truthy, then destroy is not applied to the child; otherwise destroy is applied to the child
deletedChild
Component
the deleted child (if any)

Description

If the child does not exist in the children collection, then no processing occurs and false is returned; this is not considered an error. Otherwise...

The child's dom root(s) are removed from the instances's DOM tree, false is applied to bdAttachToDoc on the child, and the child is deleted from children. If preserve is true, then the child is returned with no other processing (the child will be in the rendered state). If preserve is false, then destroy is applied to the child and false is returned.

Notice that false is returned in two cases: (1) the child does not exist in the children collection, (2) the child was destroyed. Since a destroyed Component instance has no internal state and cannot be further manipulated, there is no purpose in returning such an instance.

destroy [method]

destroys all resources and references owned by the instance

Synopsis

instance.destroy() => void

Description

Destroy all resources and references owned by the instance, thereby making the instance readily available for garbage collection. In particular, the following is accomplished:

  • Unrenders the instance (if rendered) and destroys all resources (DOM nodes, event connections, etc.) acquired during the time the component was rendered.
  • Destroys all watchers on instance properties.
  • Destroys all handlers on instance events.
  • Destroys all Destroyable instances published to own.
  • Deletes this.kwargs.

focus [method]

sets focus on the component

Synopsis

instance.focus() => void

Description

Applies the DOM node method focus() to the node in the component's DOM tree that has a non-empty tabIndex property.

insChild [method]

inserts a child component into the children collection

Synopsis

instance.insChild(element, node, position) => insertedChild
instance.insChild(ComponentSubclass, kwargs, node, position) => insertedChild
instance.insChild(child, node, position) => insertedChild
element
Element
child to be inserted is created as new Component({elements:element})
node
DOM node | string
optional
the reference node to attach the child's root(s); if a string is provided, then node is computed as document.getElementById(node); see position
position
Position
optional
if missing, defaults to "last"

first, last => attach the child's DOM root(s) as the first/last children of node

before, after => attach the child's DOM root(s) as before/after siblings of node

only => attach the child's DOM root(s) as the sole child of node after removing and destroying any existing children of node

replace => attach the child's DOM root(s) in the position that node exists with respect to it's siblings; remove and destroy node
ComponentSubclass
function
a constructor function that creates an instance of Component; child to be inserted is new ComponentSubclass(kwargs)
kwargs
ConstructorKeywordArgs
keyword arguments to be provided to ComponentSubclass during construction; if kwargs is missing, then {} is provided by default
child
instance of Component
the child to be inserted
insertedChild
instance of Component
the child that was inserted

Description

Creates a new component (if an element or ComponentSubclass was provided), deletes the child from its current parent (if child was provided that has a parent), renders the child (if necessary), inserts the child DOM root(s) into the instance DOM tree, and pushed child into children. If the instance is attached to the document, bdAttachToDoc is applied on the child.

If node is provided, then the DOM root(s) of the new child are inserted with respect to node as give by position; see the description of the position parameter, above.

If node is not provided, then the DOM root(s) of the new child are appended to the first node that exists from the following choices:

  1. child.bdParentAttachPoint; see bdParentAttachPoint
  2. this.bdChildrenAttachPoint; see bdChildrenAttachPoint
  3. this.bdDom.root; see bdDom

Note that an instance must be rendered before attempting to insert children.

own [method]

ensures Destroyable instances are destroyed when the instance is destroyed

Synopsis

instance.own(...args) => void
arg
Destroyable
Destroyable instance to be destroyed upon component destruction.
arg
(Destroyable | falsey)[]
array of Destroyable instances to be destroyed upon component destruction; any falsey elements are ignored.
arg
falsey
ignored

Description

own simplifies management of destroyable resources (e.g., the objects returned by watchHub.watch, eventHub.advise, connect) by guaranteeing such resources are destroyed upon component destruction. All methods that return destroyable instances automatically apply those instances to own. Client code can leverage own to manage destroyable resources that are created by machinery outside of Component's implementation. For example:

let child = someParent.insChild(SomeChildType, {/* ... */});
child.watch("someChildProperty", (newValue) => {/* ... */});
child.own(
    globalEventGenerator.advise(
        "someEvent", child.someMethodThatProcessesSomeEvent.bind(child)
    )
);

In the example above, notice that it was not necessary to apply own to the result of child.watch since the design of Component takes care of that task automatically; on the other hand, there is no way for child to know about the destroyable event handler that was connected by globalEventGenerator.advise; therefore, own is called explicitly. Consequent to this application, when child is destroyed, the event handler will automatically be destroyed.

ownWhileRendered [method]

Ensures Destroyable instances are destroyed when the instance is unrendered

Synopsis

instance.ownWhileRendered(...args) => void
arg
Destroyable
Destroyable instance to be destroyed upon component destruction.
arg
(Destroyable | falsey)[]
array of Destroyable instances to be destroyed upon component destruction; any falsey elements are ignored.
arg
falsey
ignored

Description

ownWhileRendered functions similarly to own except that any Destroyable instances are collected and destroyed between render and unrender.

postRender [method]

executes processing after the creation of the DOM tree(s)

Synopsis

instance.postRender() => handles
handles
Destroyable |Destroyable[]
see description

Description

Upon entry to postRender the DOM tree(s) associated with the instance have been created and their root(s) stored at bdDom.root (see bdDom). The default implementation of postRender is a no-op. See render for further details.

Any handles returned by postRender are applied to ownWhileRendered.

removeClassName [method]

removes one or more string components from className

Synopsis

instance.removeClassName(...args) => this
arg
string
a component to add
arg
(string | falsey)[]
zero or more components to add
arg
falsey
ignored

Description

Removes all of the string components provided by args from className. No error is generated when className does not contain a target component. Components are broken down to their word atoms before attempting remove, so removing "foo bar" will remove the same components as "bar foo", and either "foo bar" or "bar foo" will remove all components from either "foo bar" or "bar foo".

render [method]

creates the DOM tree and renders all children given by bdElements

Synopsis

instance.render(callback) => void
callback
() => (Destroyable | Destroyable[] | void)
optional
if provided, applied after the instance is rendered. Any Destroyable instances returned are applied to ownWhileRendered.

Description

If the instance is already in the rendered state, then no-op; otherwise:

  1. Generate the Element tree(s) by applying bdElements.
  2. Create the DOM tree(s) decribed by the Element tree(s) generated in Step 1.
  3. Store the new tree(s) at bdDom.root (see bdDom).
  4. Publish the new tree(s) in the Component catalog (see get).
  5. Apply postRender.
  6. Apply callback (if provided).

reorderChildren [method]

reorder children in-place

Synopsis

instance.reorderChildren(children) => void
children
Component[]
this.children, reordered as desired

Description

This method requires all children passed in children have the same DOM node parent for their root DOM nodes. The children root DOM nodes are reordered as given by children.

toggleClassName [method]

adds or removes one or more string components for className

Synopsis

instance.toggleClassName(...args) => this
arg
string
a component to add
arg
(string | falsey)[]
zero or more components to add
arg
falsey
ignored

Description

args is broken down into a single list of words with no repeats. For each word, w, in the list, if this.containsClassName(w) is true, then w is removed from className, otherwise, w is added to className.

unrender [method]

destroys all children and destroys the DOM tree associated with the instance

Synopsis

instance.unrender() => void

Description

If the instance is in the unrendered state, then no-op; otherwise:

  1. Deletes the Component's tree(s) from the Component catalog (see get):
  2. Deletes itself by applying delChild on itself to its parent (if any).
  3. Destroys all of its own children by applying destroy to every child in its children collection.
  4. Destroys all destroyable objects collected by ownWhileRendered.
Subclasses of Component that create children which are not managed by the children collection (an atypical practice) usually provide an override to unrender that ensures such children are properly destroyed.

protected [namespace]

protected properties and methods

Description

These properties and methods should only be used by within the implementations of subclasses of Component.

static [namespace]

static properties and methods defined on Component

types [namespace]

types used in Component machinery

children [property]

the children of the Element

Type

read-only: undefined | Element | Element[]

Description

The children of the Element.

ctorProps [property]

Hash of (property -> value) used to initialize the actualized node

Type

read-only: Hash of (property -> value)

Description

When type gives a Component subclass constructor, ctorProps gives the arguments applied to the constructor. For example, given an Element instance element where element.type===MyClass, the Component instance would be created as follows:

new MyClass(element.ctorProps)

When type gives a DOM node tag, ctroProps gives the (property -> value) pairs used to initialize the actualized node immediately after it is created., For example, given an Element instance element, and a newly created node n, n would be initialized as follows:

Reflect.ownKeys(element.ctorProps).forEach(p => setAttr(n, p, element.ctorProps[p])

ppFuncs [property]

hash of (ppfProcId -> arguments) pairs executed on a newly actualized node

Type

read-only: Hash of (ppfProcId -> arguments)

Description

Immediately after the node implied by type is instantiated and initialized by ctorProps, each post-processing-function given by ppFuncs is executed as follows:

Reflect.ownKeys(element.ppFuncs).forEach(ppfProcId =>
    getPostProcessingFunction(ppfProcId)(ppfOwner, ppfTarget, ...(element.ppFuncs[ppf]))
)

See post-processing-functions for an explanation of ppfProcId, ppfOwner, and ppfTarget. See getPostProcessingFunction.

type [property]

the type of node

Type

read-only: string | [namespace, tag] | subclass of Component constructor

Description

Describes the type of the actualized node:

  • If a string, then type gives an HTML element type, for example "div".
  • If a [namespace, tag], then namespace (a string) gives a www.w3.org namespace and tag (a string) gives the element type within that namespace, for example ["http://www.w3.org/2000/svg", "circle"].
  • Otherwise type is a subclass of Component constructor.
svg(node, args) is shorthand for new Element(["http://www.w3.org/2000/svg", node], args); see svg.

constructor

instance initializer

Synopsis

new Element(htmlTag, props = {}, ...children)
new Element(namespacedTag, props = {}, ...children)
new Element(component, props = {}, ...children)
htmlTag
string
an HTML tag name
namespacedTag
[namespace, tag]
namespace (a string) is a www.w3.org namespace;
tag (a string) gives a element tag within that namespace
component
subclass of Component constructor
component type
props
Hash
optional, default = {}
The Hash of (property -> value) and (post-processing-function -> arguments) pairs that describe how to initialize ctorProps and ppFuncs.
children
falsey | Element | children[]
optional
children can be falsey | Element | children[], thereby allowing arbitrarily nested arrays of children.

Description

Initializes a new instance.

children is flattened into a single array and all falsey values are removed.

An example of the [namespace, tag] form is ["http://www.w3.org/2000/svg", "circle"]. Backdraft provides svg to avoid this verbose form for the SVG namespace.

props is sifted into ctorProps and ppFuncs: any property p in props such that getPostProcessingFunction(p) returns a function is placed in ppFuncs; otherwise p is placed in ctorProps.

User-defined post-processing-instructions must be added to the Backdraft post-processing-instruction catalog with insPostProcessingFunction and/or replacePostProcessingFunction before creating Element nodes that reference those user-defined post-processing-instructions.

Typically, the functions e and svg are used to create element instances.

value [property]

the formatted value of the reference property

Type

any

Description

Given referenceObject, referenceProp, and formatter at construction, value is returned as follows:

if(referenceProp===WatchableRef.STAR){
    return formatter(referenceObject);
}else{
    return formatter(referenceObject[referenceProp]);
}

Recall that the formatter defaults to the identify function; see constructor.

constructor

instance initializer

Synopsis

new WatchableRef(referenceObject, referenceProp = STAR, formatter = x => x)
referenceObject
watchHub | Watchable
the reference watchable object
referenceProp
string | symbol | STAR
optional, default = STAR
the property within referenceObject to reference
formatter
function(any) => any
optional, default = x => x
applied to the reference property value to compute value and applied to newValue/oldValue args before applying watchers

Description

Initializes a new WatchableRef instance.

When referenceProp===STAR, value return formatted referenceObject and watchers are applied upon any mutation within referenceObject; see WatchableRef.

watchHub is a superclass of Component, therefore, all Component instances are watchable objects.

destroy [method]

destroys all registered watchers

Synopsis

instance.destroy() => void

Description

Destroys all registered watchers. The WatchableRef instance is not dead; other watchers can be connected by applying watch after applying destroy.

watch [method]

connect a watcher to the reference property in the reference watchable object

Synopsis

instance.watch(watcher) => handle
watcher
Watcher
applied when the formatted value of the reference property mutates
handle
Destroyable
Destroyable that terminates the watch

Description

Connects watcher so that watcher is applied when the formatted value of the reference property mutates. Note carefully that the watcher is applied only if a substantive mutation is detected, taking into account the formatter.

When watch is applied when the reference property is other than STAR, then watchers will be applied to the following arguments:

  • newValue: the value of the property after mutation
  • oldValue: the value of the property before mutation
  • prop: the same as prop provided when watch was applied to connect the watcher
  • object: the object to which watch was applied to connect the watcher

When watch is applied when the reference property is STAR, then watchers will be applied to the following arguments:

  • newValue: the value of the particular property that mutated, after mutation
  • oldValue: UNKNOWN_OLD_VALUE
  • prop: an array that lists the property path to the particular watchable property that mutated
  • object: the object to which watch was applied to connect the watcher

See example at WatchableRef.

When the following three conditions exist:

  1. the reference object is an instance of watchHub
  2. the reference object is not STAR
  3. referenceObject[referenceProp] is also a watchable

Then, any connected watchers will be signaled if either referenceObject[referenceProp] mutates or some mutation occurs within referenceObject[referenceProp].

static [namespace]

static properties and methods defined on WatchableRef

isBdEventHub [property]

returns true to indicate the instance contains the eventHub interface

Type

read-only: true

Description

Since eventHub is a mixin class (see mixins), instanceof does not function as expected. Instead, isBdEventHub can be used to test if a particular instance defines the eventHub interface. For example:

class SuperClass {};

class MyClass extends eventHub(SuperClass) {}

let test = new MyClass();
test instanceof MyClass;    // true;
test instanceof SuperClass; // true;
test instanceof eventHub;   // false;
test instanceof EventHub;   // false;
test.isBdEventHub;          // true

advise [method]

register a handler for an event

Synopsis

instance.advise(eventType, handler) => handle
instance.advise(eventTypes, handler) => handles[]
instance.advise(hash) => handles[]
event
string | symbol
the event to advise
events
[string | symbol]
list of events to advise
hash
Hash (event -> handler)
list of (event -> handler) pairs to advise
handler
function(eventObject)
handler to apply upon event
handle
Destroyable
Destroyable object that can be used to terminate the advise
handles
Destroyable[]
Destroyable objects that can be used to terminate the advise

Description

The first signature is the only substantive signature, the remaining signatures are syntax sugar discussed at the end.

Given an event object, eo, upon the owning instance applying bdNotify(eo), all handlers previously registered to eventType===eo.type are applied to eo. See example at eventHub.

All signatures return a handle or handles to all advises that were connected. If this implements the method Component.own, then all handles are owned. This ensures the advises are automatically terminated when the owning object is destroyed with Component.destroy

The remaining signatures are syntax sugar:

instance.advise(events, handler)
instance.advise(hash)

are essentially equivalent to

(instance.own || noop)(props.map(p => this.advise(p, handler))
(instance.own || noop)(Reflect.ownKeys(hash).map(p => this.advise(p, hash[p]))

We say "essentially" because the Destroyable objects created with each watch connection are returned to the caller and this detail is not depicted in the code above.

destroyAdvise [method]

destroys all registered handlers for a particular event or all events

Synopsis

instance.destroyAdvise(eventType) => void
eventType
string | symbol | undefined
destroy all watchers on a single event (if given); all events, otherwise

Description

If eventType is provided, than all handlers connected the given event are destroyed; otherwise, all handlers on all events are destroyed.

protected [namespace]

protected properties and methods

Description

These properties and methods should only be used by within the implementations of subclasses derived from eventHub.

isBdWatchHub [property]

returns true to indicate the instance contains the watchHub interface

Type

read-only: true

Description

Since watchHub is a mixin class (see mixins), instanceof does not function as expected. Instead, isBdWatchHub can be used to test if a particular instance defines the watchHub interface. For example:

class SuperClass {};

class MyClass extends watchHub(SuperClass) {}

let test = new MyClass();
test instanceof MyClass;    // true;
test instanceof SuperClass; // true;
test instanceof WatchHub;   // false;
test instanceof watchHub;   // false;
test.isBdWatchHub;          // true

destroyWatch [method]

destroys all registered watchers for a particular property or all properties

Synopsis

instance.destroyWatch(prop) => void
prop
string | symbol | undefined
optional
destroy all watchers on a single property (if given)

Description

If prop is provided, than all watchers watching the given property are destroyed; otherwise, all watchers on all properties are destroyed.

getWatchableRef [method]

create and own WatchableRef

Synopsis

instance.getWatchableRef(prop = STAR, formatter=x => x)
prop
string | symbol
optional, default = STAR
the reference property
formatter
any => any
optional, default = x => x
the formatter used by the WatchableRef instance

Description

Syntax sugar:

instance.getWatchableRef(prop, formatter)

is equivalent to

(this.own || noop)(getWatchableRef(this, prop, formatter))

watch [method]

register a watcher on a property

Synopsis

instance.watch(prop = STAR, watcher) => handle
instance.watch(props, watcher) => handles[]
instance.watch(hash) => handles[]
instance.watch(watchable, prop = STAR, watcher) => handle
instance.watch(watchable, props, watcher) => handles[]
instance.watch(watchable, hash) => handles[]
instance.watch(watchable) => handles[]
prop
string | symbol
optional, default = STAR
the property to watch
props
(string | symbol)[]
list of properties to watch
hash
Hash (property -> watcher)
list of (property -> watcher) pairs to watch
watcher
Watcher
watcher to apply upon mutation of prop
watchable
watchHub | Watchable
object to watch
handle
Destroyable
Destroyable object that can be used to terminate the watch
handles
Destroyable[]
Destroyable objects that can be used to terminate the watch

Description

The first signature is the only substantive signature, the remaining signatures are syntax sugar discussed at the end.

When watch is applied with a prop argument other than STAR, then watchers will be applied to the following arguments:

  • newValue: the value of the property after mutation
  • oldValue: the value of the property before mutation
  • prop: the same as prop provided when watch was applied to connect the watcher
  • target: the object to which watch was applied to connect the watcher

When watch is applied with a prop === STAR, then watchers will be applied to the following arguments:

  • newValue: the value of the particular property that mutated, after mutation
  • oldValue: see UNKNOWN_OLD_VALUE
  • prop: an array that lists the property path to the particular watchable property that mutated
  • target: the object to which watch was applied to connect the watcher

All signatures return a handle or handles to all watches that were connected. If this implements the method Component.own, then all handles are owned. This ensures the watches are automatically terminated when the owning object is destroyed with Component.destroy

The remaining signatures are syntax sugar:

instance.watch(props, watcher)
instance.watch(hash, watcher)
instance.watch(watchable, prop, watcher)
instance.watch(watchable, props, watcher)
instance.watch(watchable, hash)
instance.watch(watchable)

are essentially equivalent to

(instance.own || noop)(props.map(p => this.watch(p, watcher))
(instance.own || noop)(Reflect.ownKeys(hash).map(p => this.watch(p, hash[p]))
(instance.own || noop)(watch(watchable, props, watcher))
(instance.own || noop)(watch(watchable, hash))
(instance.own || noop)(watch(watchable))

We say "essentially" because the Destroyable objects created with each watch connection are returned to the caller and this detail is not depicted in the code above.

protected [namespace]

protected properties and methods

Description

These properties and methods should only be used by within the implementations of subclasses derived from eventHub.

bdCollection [protected property]

the protected property that holds the actual collection value

Type

read-write: array

Description

bdCollection is reflected and mutated by collection.

This is a protected property and is not intended to be accessed by client code.

bdSynchChildren [protected method]

insert/delete children to match collection

Synopsis

instance.bdSynchChildren() => void

Description

When rendered inserts or deletes children as required so there is a 1-to-1, onto map between the items in collection and children components.

bdCollectionIndex [protected property]

the protected property that holds the actual collectionIndex value

Type

read-write: array

Description

bdCollectionIndex is reflected and mutated by collectionIndex.

This is a protected property and is not intended to be accessed by client code.

bdCollectionItem [protected property]

the protected property that holds the actual collectionItem value

Type

read-write: array

Description

bdCollectionItem is reflected and mutated by collectionItem.

This is a protected property and is not intended to be accessed by client code.

withWatchables [static method]

withWatchables with additional property processing functionality

Type

mixin

Description

CollectionChild.withWatchables is an extension to withWatchables that allows the additional property specifier:

"item: prop [,prop] [,prop] ..."

Each prop, "pname", causes a watchable property "pname" to be implemented on the resulting subclass that reflects the the "pname" property in this.parent.collection[this.collectionIndex].

When CollectionChild.withWatchables is not provided a superclass for its first argument, it assumes CollectionChild.

For example, given a collection defined as follows:

let stats = toWatchable([
    {label: "A", value: 100},
    {label: "B", value: 100}
]);

withWatchables could be used to defined the class StatChild, a subclass of CollectionChild, that defines the watchable property "value" which reflects the "value" property of the particular item in stats to which a particular StatChild instance is associated as follows:

class StatChild extends CollectionChild.withWatchables("item:value"){
}

The Backdraft Polygraph example uses CollectionChild.withWatchables extensively. You can explore Polygraph in this Pen or load the example directly into your browser.

bdAttachedToDoc [protected property]

the protected property that holds the actual attachedToDoc value

Type

read-write: boolean | undefined

Description

bdAttachedToDoc is reflected by attachedToDoc; bdAttachedToDoc is mutated by internal, protected processes, primarily bdAttachToDoc.

This is a protected property and is not intended to be accessed by client code.

bdChildrenAttachPoint [protected property]

the location within a parent to attach children

Type

read-write: Component | undefined

Description

Gives the property address within the component instance that holds the DOM node to which the component should append children DOM roots.

This is a protected property and is not intended to be accessed by client code.

bdClassName [protected property]

the protected property that holds the actual className value

Type

read-write: string | undefined

Description

bdClassName is mutated by bdSetClassName; bdClassName is reflected by className.

This is a protected property and is not intended to be accessed by client code.

bdDisabled [protected property]

the protected property that holds the actual disabled value

Type

read-write: boolean

Description

bdDisabled is reflected by both disabled and enabled.

This is a protected property and is not intended to be accessed by client code.

bdDom [protected property]

the protected property that holds various references pertaining to a component's rendering

Type

read-write: object

Description

bdDom holds various references/data pertaining to a component's rendering. Component defines the following properties:

  • bdDom.root: the root DOM node or nodes of the component's rendering
  • bdDom.titleNode: the DOM node to which the title property is reflected (title is reflected to bdDom.titleNode.title)
  • bdDom.tabIndexNode: the DOM node to which the tabIndex property is reflected (tabIndex is reflected to bdDom.tabIndexNode.tabIndex)
  • bdDom.handles: the list of Destroyable objects that have been ownWhileRendered.

Subclasses of Component are free to add to bdDom so long as they do not step on already-existing definitions.

This is a protected property and is not intended to be accessed by client code.

bdHasFocus [protected property]

the protected property that holds the actual hasFocus value

Type

read-write: boolean | undefined

Description

bdHasFocus is reflected by hasFocus; bdHasFocus is mutated by internal, protected processes, primarily bdOnFocus and bdOnBlur.

This is a protected property and is not intended to be accessed by client code.

bdParent [protected property]

the protected property that holds the actual parent value

Type

read-write: Component | undefined

Description

bdParent is reflected by parent; bdParent is mutated by internal, protected processes, primarily bdAdopt.

This is a protected property and is not intended to be accessed by client code.

bdParentAttachPoint [protected property]

the location within a parent to attach the component

Type

read-write: string | symbol

Description

Gives the property address within a prospective parent object that holds the DOM node to which the component's DOM root should be appended.

This is a protected property and is not intended to be accessed by client code.

bdTabIndex [protected property]

the protected property that holds the actual tabIndex value

Type

read-write: number | falsey

Description

bdTabIndex is reflected by tabIndex

This is a protected property and is not intended to be accessed by client code.

bdTitle [protected property]

the protected property that holds the actual title value

Type

read-write: string | undefined

Description

bdTitle is reflected by title

This is a protected property and is not intended to be accessed by client code.

bdAdopt [protected method]

accomplish various internal bookkeeping tasks when a child is added to a component

Synopsis

instance.bdAdopt(child) => void
child
Component
the child that is being adopted

Description

  1. Push the child into children.
  2. Mutate the child's parent property to reflect the new parent.
  3. If this.attachedToDoc===true, than apply true to bdAttachToDoc on the child.

bdAttachToDoc [protected method]

mutates the property bdAttachedToDoc; applied recursively to all children

Synopsis

instance.bdAttachToDoc(attach) => boolean
attach
boolean
the new value of bdAttachedToDoc

Description

If attach!==bdAttachedToDoc, then mutate bdAttachedToDoc to the new value, recursively apply to all children, and return true; otherwise, no-op and return false.

bdElements [protected method]

generate the Element tree(s) that describe the components DOM

Synopsis

instance.bdElements() => Element | Element[]

Description

bdElements is applied immediately before rendering the instance to create the Element tree(s) that describe the structure, properties, and connections of the DOM tree(s) associated with the instance. Typically, classes derived from Component override bdElements to define a particular structure as required by the semantics and design of the subclass. bdElements may also be overridden on a per-instance basis by providing kwargs.elements at construction or setting the instance bdElement method explicitly.

The default implementation generates the Element tree new Element("div", {}).

bdOnBlur [protected method]

applied by focusManager when the component loses the focus

Synopsis

instance.bdOnBlur() => void

Description

This protected method is applied by the focusManager when the component has the focus and then the focus moves to a component other than itself or one if its descendants. The default processing sets bdHasFocus to false and executes this.removeClass("bd-focused").

This is a protected method and is not intended to be accessed by client code.

bdOnFocus [protected method]

applied by focusManager when the component or one of its descendants receives the focus

Synopsis

instance.bdOnFocus() => void

Description

This protected method is applied by the focusManager when a DOM node within the component's DOM or one of its descendants' DOM receives the focus. The default processing sets bdHasFocus to true and executes this.addClassName("bd-focused").

This is a protected method and is not intended to be accessed by client code.

bdSetClassName [protected method]

mutates bdClassName and signals all className watchers

Synopsis

instance.bdSetClassName(newValue, oldValue) => void

Description

Mutates bdClassName and signals all className watchers.

This is a protected method and is not intended to be accessed by client code.

events [static property]

list of events defined by Component

Type

(string | symbol)[]

Description

As a best practice, all Backdraft-defined classes derived from eventHub define the list of event names which the class may notify. This list is a static property located at events.

watchables [static property]

list of watchable properties defined by Component

Type

(string | symbol)[]

Description

As a best practice, all Backdraft-defined classes derived from watchHub define the list of property names which are watchabe. This list is a static property located at watchables.

get [static method]

resolve a Component instance from a DOM node

Synopsis

Component.get(node) => Component | undefined

Description

Given a DOM node that is the root of a Component instance, returns that instance; undefined otherwise.

withWatchables [static method]

withWatchables with superclass===Component

Type

mixin

Description

Syntax sugar:

class MyClass extends Component.withWatchables(propertyDefs) { /* ... */ }

is equivalent to

class MyClass extends withWatchables(Component, propertyDefs) { /* ... */ }

It's not shorter, but it reads better.

ConstructorKeywordArgs [type]

keyword-arguments consumed by Component's constructor

Definition

interface ConstructorKeywordArgs {
    id?: any truthy value convertible to string;
    staticClassName?: string;
    className?: string | string[];
    tabIndex?: number | falsey;
    title?: string;
    disabled?: boolean;
    enabled?: boolean;
    elements?: Element | Element[] | () => (Element | Element[]);
    postRender?: () => (Destroyable | Destroyable[] | void);
    overrides?: Hash;
    callbacks?: Hash;
    mix?: Hash;
    [string]: any
    [symbol]: any
}

Description

See Component.constructor.

STAR [static property]

reference to STAR

Type

symbol

Description

See constructor

UNKNOWN_OLD_VALUE [static property]

reference to UNKNOWN_OLD_VALUE

Type

symbol

Description

See UNKNOWN_OLD_VALUE.

bdNotify [protected method]

notify registered handlers of an event

Synopsis

instance.bdNotify(eo) => void
eo
Object
any object with the property type

Description

All handlers previously registered to event type eo.type are applied to eo.

bdMutate [protected method]

mutate a property and notify watchers

Synopsis

instance.bdMutate(publicName, privateName, newValue) => void
instance.bdMutate(list) => void
publicName
string | symbol
a property name registerable with watch
privateName
string | symbol
the property name that holds the actual value
newValue
any
the value to assign to the property
list
array of [publicName, privateName, newValue]
see description

Description

bdMutate compares this[privateName] to newValue using eql. If a mutation is detected, this[privateName] is set to newValue and all watchers registered on publicName are applied via bdMutateNotify.

A list of triples is used to give the illusion that several property mutations occur atomically. First, each property is mutated, then, each mutation is notified. This algorithm results in applying watchers after all mutations have been completed, thereby preventing watchers from being applied when the underlying object is in a possibly-illegal state.

bdMutateNotify [protected method]

notify watchers of a mutation

Synopsis

instance.bdMutateNotify(prop, newValue, oldValue) => void
instance.bdMutateNotify(list) => void
prop
string | symbol
a property name registerable with watch
newValue
any
the value of the mutated property, after the mutation
oldValue
any
the value of the mutated property, before the mutation
list
array of [publicName, newValue, oldValue]
see description

Description

Applies (newValue, oldValue, this, prop) to all watchers connected via watch to the property prop. A list of triples is used to signal several mutations at once:

this.bdMutateNotify(list)

is equivalent to

list.forEach(item => this.bdMutateNotify(...item))

This signature can be used to give the illusion that several different properties mutated atomically. See Watchable Properties for details.