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)
geometry
is 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 oftracking
:if
tracking === false
,s
is a selection mapping: a function from element indices to booleans. For all element indicesi
,s(i)
istrue
ifi
is selected ins
, otherwisefalse
.Assuming the element indices are indices to an array named
elements
and that the functionvisualize(e, b)
shows the elemente
as selected or not, depending on whetherb
istrue
orfalse
. A simplerefresh
implementation could then be:function refresh(s) { for (var i=0; i<elements.length; ++i) visualize(elements[i], s(i)); }
if
tracking === true
,s
is a built-inMap
(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 theSelectionState
object’s methods calledrefresh
(or since the object’s construction).With the same assumptions as above, a simple
refresh
implementation could be:function refresh(s) { s.forEach(function (value, i) { visualize(elements[i], value); }); }
tracking
controls whether change tracking should be used or not (the default isfalse
: no tracking)maxundo
is 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
SelectionState
object 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
i
is an index to an element. Returnstrue
if the element is selected,false
if not.
SelectionState.selected();
- Returns the indices of the selected elements as a
Set
(ECMAScript 6).
SelectionState.onSelected(vpoint);
- Returns
true
ifvpoint
is on exactly one element and that element is selected. Concretely, the result is computed as follows. A variablepath
is 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.
click
unselects all currently selected elements;cmdClick
does not.- The current selection path
_spath
is set to[]
geometry().extendPath(_spath, vpoint)
is called.- If
extendPath
returns anything other thannull
, the keyboard cursor is set tovpoint
. - The new selection domain is computed with the call
selectionPath(_spath, undefined)
. - The
refresh
callback is called (if there are no elements whose selection state changes, the call might not be made). - The
selmode
parameter is a boolean and it determines whethercmdClick
should select or deselect elements. Typically it isundefined
, in which casecmdClick
function selects ifvpoint
is on a selected element, and deselects if not.
SelectionState.shiftClick(vpoint);
geometry().extendPath(_spath, vpoint)
is called.- If
extendPath
returnsnull
, 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)
, whereJ
is the current selection path. - The
refresh
callback 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
refresh
function is invoked.
The undo
method does not modify the keyboard cursor.
SelectionState.undo();
- The effect of the most recent call to
undo
is undone. - The selection path is set to
[]
. - The
refresh
function 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
state
is 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
select
or 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
cmdSpace
andshiftSpace
functions 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
dir
parameter 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)
, wherec
is the current cursor. TheshiftArrow
function invokescmdSpace
prior to updating the cursor whereas thecmdArrow
function invokescmdSpace
after 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
), thenshiftArrow
callsshiftSpace(dir)
andcmdArrow
callscmdSpace(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
, ofshiftClick
functions. We makem2v
a method of the selection geometry and insist thatm2v
function 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
spath
with the selection space pointvpoint
. - To communicate results to the caller,
extendPath(spath, vp)
modifies thespath
parameter. Ifspath
is not modified at all,extendPath(spath, vp)
should returnnull
. - The
spath
parameter is an array of selection state points. Typicallyvpoint
is added at the end ofspath
, but this is not a requirement:extendPath
is allowed to modifyspath
array in arbitrary ways. For example, certain values ofvpoint
could be interpreted as commands that remove or move points inspath
. - Hint:
spath
can be non-empty only when invoked fromshiftClick
. Two consecutive calls toextendPath
wherespath
is non-empty can only come from two consecutive calls toshiftClick
.
selectionDomain(spath, J)
- This function should compute the selection domain that
spath
gives rise to. - If
J
is notundefined
, it is the current selection domain (computed by the previous call toselectionDomain
). The object referenced to byJ
can 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 toselectionDomain
it suffices to inspect points from that index forward. - The
selectionDomain
function is called fromclick
,cmdClick
,shiftClick
, andonSelected
functions. TheJ
parameter is defined only when called fromshiftClick
. - Two consecutive calls to
selectionDomain
whereJ
is defined can only come from two consecutive calls toshiftClick
that operate on the same active set. IfJ
is 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
dir
parameter’s value is one of the library’s constantsUP
,DOWN
,LEFT
, orRIGHT
. If movement to some direction is not supported,step
should returnvpoint
. - The library never calls
step
withundefined
value forvpoint
.
defaultCursor(dir)
- This function should return the default location of the keyboard cursor
for each
dir
value. If there is no default for some direction, the return value must beundefined
. dir
is one ofUP
,DOWN
,LEFT
, orRIGHT
,NO_DIRECTION
. TheNO_DIRECTION
value indicates thatdefaultCursor
was called from one ofspace
,shiftSpace
, orcmdSpace
functions. The other four values indicate that it was called from one of the arrow functions.
filter(pred)
- This function should return a
Map
whose keys are the indicesi
for 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
anchor
function returns the first element of thepath
array, orundefined
ifpath
is empty.
function activeEnd(path)
- The
activeEnd
function returns the last element of thepath
array, orundefined
ifpath
is empty.
function makeEmptyMap()
- The
selectionDomain(path, J)
method in the selection geometry classes may need to construct a new selection domain. ThemakeEmptyMap
function 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.