MultiselectJS API
Table of Contents
1 Introduction
This document describes the API of the MultiselectJS library. The document assumes understanding of the following concepts: selection geometry, selection mapping, selection domain, active set, selection path, anchor, active end, and keyboard cursor. Please read the MultiselectJS tutorial or/and the paper One Way to Select Many if these concepts are not familiar to you.
The library components are defined as CommonJS modules.  They are
multiselect.js, default_geometry.js, dom_geometries.js, and
ordered_geometries.js. The main module is multiselect.js. The
other modules define selection geometry classes, intended to be used
as base classes for client-defined selection geometries. The main
module depends on (requires) default_geometry.js, but the latter
is still defined as separate module. This is so that modules that
define selection geometries do not need to require multiselect.js;
requiring default_geometry.js suffices.
2 multiselect.js
The names exported by multiselect.js are:
| SelectionState | 2.1 | 
| DefaultGeometry | 2.2.1 | 
| anchor,activeEnd | 2.3.1 | 
| makeEmptyMap | 2.3.1 | 
| UP,DOWN,LEFT,RIGHT,NO_DIRECTION | 2.3.2 | 
| modifierKeys | 2.3.3 | 
| NONE,SHIFT,CMD,SHIFT_CMD,OPT,SHIFT_OPT | 2.3.3 | 
2.1 The SelectionState class
The SelectionState class maintains all of the state of the
selection, which includes the selection status of each element, the
current selection path, undo and redo stacks, and keyboard cursor.
2.1.1 Construction
The SelectionState class has one constructor:
SelectionState(geometry, refresh, tracking, maxundo)
- geometryis the selection geometry. This object must satisfy the requirements laid out in Section 2.2.
- refresh(s)is a callback function that visualizes the current selection state of the elements. With what kind of argument the library invokes the callback depends on the value of- tracking:- if - tracking === false,- sis a selection mapping: a function from element indices to booleans. For all element indices- i,- s(i)is- trueif- iis selected in- s, otherwise- false.- Assuming the element indices are indices to an array named - elementsand that the function- visualize(e, b)shows the element- eas selected or not, depending on whether- bis- trueor- false. A simple- refreshimplementation could then be:- function refresh(s) { for (var i=0; i<elements.length; ++i) visualize(elements[i], s(i)); } 
- if - tracking === true,- sis a built-in- Map(ECMAScript 6) whose keys are the selectable elements’ indices and values are booleans. The map has entries for exactly those elements that were changed since the previous time any of the- SelectionStateobject’s methods called- refresh(or since the object’s construction).- With the same assumptions as above, a simple - refreshimplementation could be:- function refresh(s) { s.forEach(function (value, i) { visualize(elements[i], value); }); } 
 
- trackingcontrols whether change tracking should be used or not (the default is- false: no tracking)
- maxundois the maximum number of undo operations. The default is 10, the minimum is 1 (smaller values are ignored).
2.1.2 Reset
SelectionState.reset();
- Resets the SelectionStateobject to the same state in which it was immediately after construction.
2.1.3 Accessing the selection state of elements
Access to elements’ selection state is provided by three functions: isSelected,
selected, and onSelected.
SelectionState.isSelected(i);
- The parameter iis an index to an element. Returnstrueif the element is selected,falseif not.
SelectionState.selected();
- Returns the indices of the selected elements as a Set(ECMAScript 6).
SelectionState.onSelected(vpoint);
- Returns trueifvpointis on exactly one element and that element is selected. Concretely, the result is computed as follows. A variablepathis set to[],geometry().extendPath(path, vpoint)is invoked for its effect onpath, and then the result ofgeometry().selectionDomain(path)is observed. If that result has exactly one element and that element is selected, thenonSelected(vpoint)returnstrue, otherwisefalse.
2.1.4 Accessing selection geometry
SelectionState.geometry();
- Returns a reference to the current selection geometry.
SelectionState.setGeometry(geometry);
- Replaces the current selection geometry with geometry. Prior to setting the new geometry, the current active set or filter is committed.
2.1.5 Accessing selection path and cursor
The selection path and cursor can be queried:
SelectionState.selectionPath(); SelectionState.cursor();
- These functions return a reference to the current, respectively, selection path and keyboard cursor.
The current selection path can be reset to the empty path.
This function needs to be called if the locations of elements
change and thus impact the result of the selectionDomain method.
SelectionState.resetPath();
- Commits the current active set (whether it was constructed by one of the clicks or by 
a filter). Resets the current selection path to []and keyboard cursor toundefined.
2.1.6 Click functions
All click functions take a vpoint parameter, which is
a selection space coordinate. The click and cmdClick are quite similar:
SelectionState.click(vpoint); SelectionState.cmdClick(vpoint, selmode);
- The current active set is committed.
- clickunselects all currently selected elements;- cmdClickdoes not.
- The current selection path _spathis set to[]
- geometry().extendPath(_spath, vpoint)is called.
- If extendPathreturns anything other thannull, the keyboard cursor is set tovpoint.
- The new selection domain is computed with the call
selectionPath(_spath, undefined).
- The refreshcallback is called (if there are no elements whose selection state changes, the call might not be made).
- The selmodeparameter is a boolean and it determines whethercmdClickshould select or deselect elements. Typically it isundefined, in which casecmdClickfunction selects ifvpointis on a selected element, and deselects if not.
SelectionState.shiftClick(vpoint);
- geometry().extendPath(_spath, vpoint)is called.
- If extendPathreturnsnull, there is no effect (other than the possible modifying of_spath).
- Otherwise,
the keyboard cursor is set to the last element of 
(the modified) _spath.
- The new selection domain is computed with the call
selectionDomain(_spath, J), whereJis the current selection path.
- The refreshcallback is called (if there are no elements whose selection state changes, the call might not be made).
The shiftClick function does not execute atomically.
After it has has modified the selection
path, it schedules the rest of its tasks as a separate function
to be executed later (at timeout 0)—unless 
such a function has already been scheduled. 
The selection path can thus be extended (or otherwise
modified) by extendPath calls several times in between two calls to 
selectionDomain().
2.1.7 Undo and redo
The undo and redo functionality is provided by undo and redo methods.
SelectionState.undo();
- The effect of the most recent click, keyboard command, or committed filter operation is undone.
- The selection path is set to [].
- The refreshfunction is invoked.
The undo method does not modify the keyboard cursor.
SelectionState.undo();
- The effect of the most recent call to undois undone.
- The selection path is set to [].
- The refreshfunction is invoked.
The redo method does not modify the keyboard cursor.
2.1.8 Filtering
The filter function computes a selection domain based on a predicate
over the element indices.
SelectionState.filter(predicate, state);
- The selection path is set to [].
- If stateis defined andfalse, then the filter is set to deselect, otherwise to select.
- A new active set is computed with geometry().filter(predicate).
- If another filter is already active, and the
new filter and the current filter are both set to selector both todeselect, then the current active set is replaced with the new one. Otherwise the current active set is committed before setting the new active set.
SelectionState.commit();
- The current active set is committed, which creates an undoable state.
2.1.9 Functions for keyboard commands
The library provides three functions that select elements based on the
current keyboard cursor position (space, cmdSpace, and shiftSpace),
and three functions that alter the cursor position, and/or select
elements (arrow, shiftArrow, and cmdArrow).
SelectionState.space(); SelectionState.cmdSpace(); SelectionState.shiftSpace();
- If the cursor is not defined, these functions attempt to establish a cursor from
the geometry’s default, using the call
geometry().defaultCursor(NO_DIRECTION).
- If the cursor is or becomes defined, the corresponding 
click(cursor),cmdClick(cursor), orshiftClick(cursor)function is called; otherwise there is no effect.
- The cmdSpaceandshiftSpacefunctions take a parameter (direction) that is used internally by the library; client calls to these functions should have no arguments.
SelectionState.arrow(dir); SelectionState.shiftArrow(dir); SelectionState.cmdArrow(dir);
- The dirparameter must be one of the library’s constantsUP,DOWN,LEFT, orRIGHT.
- If the cursor is defined, a new cursor is computed by
geometry().step(dir, c), wherecis the current cursor. TheshiftArrowfunction invokescmdSpaceprior to updating the cursor whereas thecmdArrowfunction invokescmdSpaceafter updating the cursor.
- If the cursor is not defined, these functions try to establish a
cursor with the call geometry().defaultCursor(dir). If a cursor can be established (defaultCursor(dir)returns something other thanundefined), thenshiftArrowcallsshiftSpace(dir)andcmdArrowcallscmdSpace(dir). If a cursor cannot be established, there is no effect.
2.2 Selection geometry objects
Typically classes that define selection geometries inherit from 
the defaultGeometry class discussed in Section 2.2.1.
A selection geometry object must define the following methods, 
conforming to the requirements listed below. 
All of the methods, except m2v, are callbacks for the library and not
intended to be called by the client.
m2v(mpoint)
- Transforms mpoint, a point in mouse coordinates, to a point in selection space.
- This function is never called by any of the
SelectionState’s methods, rather it should be called by the client in the event handlers of the various click events, prior to invokingclick,cmdClick, ofshiftClickfunctions. We makem2va method of the selection geometry and insist thatm2vfunction is used to perform the coordinate transformation so that event handling code could be reused across selection geometries.
extendPath(spath, vpoint)
- Extends the selection path spathwith the selection space pointvpoint.
- To communicate results to the caller, extendPath(spath, vp)modifies thespathparameter. Ifspathis not modified at all,extendPath(spath, vp)should returnnull.
- The spathparameter is an array of selection state points. Typicallyvpointis added at the end ofspath, but this is not a requirement:extendPathis allowed to modifyspatharray in arbitrary ways. For example, certain values ofvpointcould be interpreted as commands that remove or move points inspath.
- Hint: spathcan be non-empty only when invoked fromshiftClick. Two consecutive calls toextendPathwherespathis non-empty can only come from two consecutive calls toshiftClick.
selectionDomain(spath, J)
- This function should compute the selection domain that spathgives rise to.
- If Jis notundefined, it is the current selection domain (computed by the previous call toselectionDomain). The object referenced to byJcan be used as such, and modified and returned as the result. The purpose of making the previous selection domain available is so that the selection geometry can compute the new selection domain faster. For example, in a “snake” geometry, the selection geometry could cache the index of the last element of the selection path, so that on the next call toselectionDomainit suffices to inspect points from that index forward.
- The selectionDomainfunction is called fromclick,cmdClick,shiftClick, andonSelectedfunctions. TheJparameter is defined only when called fromshiftClick.
- Two consecutive calls to selectionDomainwhereJis defined can only come from two consecutive calls toshiftClickthat operate on the same active set. IfJis not defined, cached values should be cleared.
step(dir, vpoint)
- The function should compute a new point that is one “step” to the
given direction from vpoint. The function defines the effect of each arrow key on the cursor position.
- The dirparameter’s value is one of the library’s constantsUP,DOWN,LEFT, orRIGHT. If movement to some direction is not supported,stepshould returnvpoint.
- The library never calls stepwithundefinedvalue forvpoint.
defaultCursor(dir)
- This function should return the default location of the keyboard cursor
for each dirvalue. If there is no default for some direction, the return value must beundefined.
- diris one of- UP,- DOWN,- LEFT, or- RIGHT,- NO_DIRECTION. The- NO_DIRECTIONvalue indicates that- defaultCursorwas called from one of- space,- shiftSpace, or- cmdSpacefunctions. The other four values indicate that it was called from one of the arrow functions.
filter(pred)
- This function should return a Mapwhose keys are the indicesifor whichpred(i)is true. The value for each such index in the map should betrue.
2.2.1 DefaultGeometry
To help the client in defining selection geometries, MultiselectJS defines
the DefaultGeometry class as follows:
DefaultGeometry.prototype = { m2v : function (mp) { return mp; }, extendPath : function (spath, vp) { spath.push(vp); }, step : function (dir, vp) { return undefined; }, selectionDomain : function(spath, J) { var m = makeEmptyMap(); for (var i of spath) m.set(i, true); return m; }, defaultCursor : function(dir) { return undefined; }, filter : undefined };
2.3 Helper definitions
2.3.1 Helpers for selection paths and selection domains
function anchor(path)
- The anchorfunction returns the first element of thepatharray, orundefinedifpathis empty.
function activeEnd(path)
- The activeEndfunction returns the last element of thepatharray, orundefinedifpathis empty.
function makeEmptyMap()
- The selectionDomain(path, J)method in the selection geometry classes may need to construct a new selection domain. ThemakeEmptyMapfunction constructs the kind of object (a built-inMap) that the library expects.
2.3.2 Constants for arrow key directions
The constants that specify the directions of the four arrow keys are
UP; DOWN; LEFT; RIGHT; NO_DIRECTION;
The first four should be used in implementing the selection geometry’s
step function and used as parameters to SelectionState’s arrow,
shiftArrow, and cmdArrow functions when they are called from
client’s event handlers.  The fifth value NO_DIRECTION can be
recognized in the defaultCursor function to give a default cursor
position when the space functions are invoked with an undefined
cursor.
2.3.3 Helpers for event handlers
To figure out which modifier keys were pressed
during a shift-click or a keypress is a little tricky. 
MultiselectJS implements the logic in the modifierKeys(event) function. 
modifierKeys(event)
The event is assumed to be JavaScript’s 
KeyboardEvent or MouseEvent.
The table below shows how different key combinations 
are mapped to the result. The first matching combination
is selected (so that, e.g., shift+cmd+opt is interpreted
as shift+cmd). 
| Modifiers | Return value | 
|---|---|
| shift+cmd | SHIFT_CMD | 
| cmd | CMD | 
| shift+opt | SHIFT_OPT | 
| opt | OPT | 
| shift | SHIFT | 
| anything else | NONE | 
The cmd modifier in OSX corresponds to the ctrl modifier in Windows/Linux, and the opt modifier corresponds to alt.
The constants NONE, SHIFT, CMD, SHIFT_CMD, OPT, and
SHIFT_OPT are part of the public API of the library.