MultiselectJS API
Table of Contents
- 1. Introduction
- 2.
multiselect.js
- 2.1. The
SelectionState
class- 2.1.1. Construction
- 2.1.2. Accessing the selection state of elements
- 2.1.3. Accessing and setting selection geometry
- 2.1.4. Commands to election storage
- 2.1.5. Accessing selection path and cursor
- 2.1.6. Click functions
- 2.1.7. Modify the selection path
- 2.1.8. Undo and redo
- 2.1.9. Selecting based on a predicate
- 2.1.10. Functions for keyboard commands
- 2.2. Selection geometry objects
- 2.3. Selection storage objects
- 2.4. Helper definitions
- 2.1. The
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 domain, selection path, anchor, active end, keyboard cursor, and selection storage. Please read the MultiselectJS tutorial and/or the paper One Way to Select Many if these concepts are not familiar to you.
The library consists of just one file: multiselect.js
.
2 multiselect.js
The multiselect.js
library defines one variable: multiselect
, which
defines the following members:
SelectionState |
2.1 |
DefaultGeometry |
2.2.1 |
anchor , activeEnd |
2.4.1 |
UP , DOWN , LEFT , RIGHT , NO_DIRECTION |
2.4.2 |
modifierKeys |
2.4.3 |
NONE , SHIFT , CMD , SHIFT_CMD , OPT , SHIFT_OPT |
2.4.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 that takes five
parameters. All but the first, geometry
, has a default value and can
be left undefined.
SelectionState(geometry, refresh, tracking, maxundo, storage)
geometry
is the selection geometry. This object must satisfy the requirements laid out in Section 2.2.refresh(s, c)
is a callback function that visualizes the current selection state of the elements. The first arguments
is the selection state object. The value of the second argument depends on the value oftracking
:- if
tracking === false
,c
isundefined
. - if
tracking === true
,c
indicates the elements that were changed by the issued selection command. The type ofc
is determined by the selection storage object. By default it 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. The value of each entry gives the new selection state of the element. In other words, if the elementi
was changed, thenc.get(i)
is the new value of that element.
The default value of
refresh
is the empty function that does nothing.- if
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).storage
is the selection storage. This object must satisfy the requirements laid out in Section 2.3. The default value is a selection storage that usesMap
to present selection domains: the keys are the indices in the domain.
2.1.2 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 defined by the
selection storage. By default, the return value is a
Map
whose keys are exactly the indices of the selected elements, and values are alltrue
.
SelectionState.onSelected(vpoint);
- This method computes a selection domain as if
vpoint
was clicked. LetJ
be that selection domain. Returns the result of callingonSelected(J)
method of the selection storage object. For the default selection storage, returnstrue
ifJ
contains exactly one element and that element is selected.
2.1.3 Accessing and setting 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 selection domain or predicate is committed.
2.1.4 Commands to election storage
SelectionState.modifyStorage(cmd);
- Delegates to the
modifyStorage
method of the selection storage. The accepted values ofcmd
are defined by the selection storage.
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.
2.1.6 Click functions
SelectionState.click(vpoint);
- The
vpoint
parameter is a point in the selection space. - The current active domain is committed.
- The currently selected elements are unselected.
- The current selection cache is reset to
{}
, the current selection path is set to[]
and then extended with the geometry’sextendPath
function. - The cursor is set according to
extendPath
, or by default to the active end. - A new selection domain is computed with the
selectionPath
function. - The
refresh
callback is called (if there are no elements whose selection state changes, the call might not be made).
SelectionState.cmdClick(vpoint, selmode);
- The
vpoint
parameter is a point in the selection space. - 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. - The current active domain is committed.
- The current selection cache is reset to
{}
, the current selection path is set to[]
and then extended with the geometry’sextendPath
function. - The cursor is set according to
extendPath
, or by default to the active end. - A new selection domain is computed with the
selectionPath
function. - The
refresh
callback is called (if there are no elements whose selection state changes, the call might not be made).
SelectionState.shiftClick(vpoint);
- The selection path is extended by
vpoint
according toextendPath
. - If
extendPath
returnsnull
, there is no effect (other than the possible modifying of_spath
and_cursor
). - The cursor is set according to
extendPath
, or by default to the active end. - The new selection domain is computed with the call
selectionDomain(_spath, J, cache)
, whereJ
is the current active domain andcache
the current cache. - 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 Modify the selection path
SelectionState.modifyPath(vpoint);
This function is for updating the path and cursor, for example if the indexed family of elements has been changed so that the points on the selection path or the cursor need to be adjusted. The function always executes a shift-click that may be pending, so that the current selection domain is guaranteed to be computed based on the current selection path.
The modifyPath(vp)
invokes extendPath
in the same way as
shiftClick
. The difference is that modifyPath
does not
schedule a computation of a new active domain.
SelectionState.resetPath();
This function resets the current selection path to the empty path.
This function needs to be called if the locations of elements change
in a way that would impact the result of the selectionDomain
method.
- Commits the current active domain (whether it was computed based on
a path or a predicate). Resets the current selection path to
[]
and keyboard cursor toundefined
.
2.1.8 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 predicate selection operation is undone.
- The selection path is set to
[]
. - The
refresh
function is invoked.
The undo
method does not modify the keyboard cursor.
SelectionState.redo();
- 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.9 Selecting based on a predicate
The predicateSelect
function computes a selection domain based on a predicate
over the element indices.
SelectionState.predicateSelect(predicate, state);
- The selection path is set to
[]
. - If
state
is defined andfalse
, then the method deselects elements, otherwise it selects elements. - A new active domain is computed with
geometry().filter(predicate)
. - If another predicate is already active, and the new and the current
predicate are both set to
select
or both todeselect
, then the current active domain is replaced with the new one. Otherwise the current active domain is committed before setting the new active domain.
SelectionState.commit();
- The current active domain is committed, which creates an undoable state.
2.1.10 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(path, vpoint, cache, cursor)
- Extends
path
, an array representing the selection path, with the selection space pointvpoint
. - The
cache
argument is an object to which the selection geometry can store data between subsequent calls toextendPath
andselectionDomain
functions to optimize the selection domain calculations. - The results are conveyed to the caller as follows:
- Modifications to
path
are visible to the caller. - To indicate to the caller that the path did not change in a way
that would require recomputing the selection domain, return
null
or an objecto
whereo.path
isnull
. - To completely replace the current selection path with some new
path array
path2
, return an objecto
for whicho.path
ispath2
. - To indicate that the cursor should be set to some new value
cursor2
, return an objecto
for whicho.cursor
iscursor2
. (Otherwise, cursor will be set to the last element of the path.)
- Modifications to
- The
extendPath
function is allowed to change the path and cursor in arbitrary ways.
selectionDomain(path, J, cache)
- This function should compute the selection domain that
path
gives rise to. - The type of the selection domain object is determined by what the
selection storage requires. With the default selection storage,
the object must be a
Map
whose keys are the indices in the domain. - If
J
is notundefined
, it is the current selection domain computed by the previous call toselectionDomain
. The object referenced to byJ
can be 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. - 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 domain. Then the also thecache
object is the same for both of those calls, and for calls toextendPath
in between.
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 selection domain consisting of those
indices
i
for whichpred(i)
is true.
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 (pc, vp) { pc.path.push(vp); pc.cursor = vp; }, selectionDomain : function(spath, J) { var m = new Map(); for (var i of spath) m.set(i, true); return m; }, step : function (dir, vp) { return undefined; }, defaultCursor : function(dir) { return undefined; }, filter : undefined };
2.3 Selection storage objects
This section describes the required functionality of a selection
storage object. We assume that storage
is a selection storage
representing \(\textit{ops}(s_b)\), i
an index to an element, \(T_J\)
the type used for representing domains of primitive selection
operations, \(J\) a selection domain of type \(T_J\), and op
a primitive
selection operation whose domain is represented using \(T_J\). The
following expressions must be valid, and they must have the semantics
as described below:
storage.at(i)
- Returns \(\textit{ops}(s_b)(i)\).
storage.selected()
- Returns the set of indices of the selected elements, that is \(\{i \in I\ |\ \textit{ops}(s_b)(i)\}\), as an object of type \(T_J\).
storage.push(op, changed)
Adds a new primitive selection operation
op
to the front of the op-composition. Ifstorage
represents \(\textit{ops}(s_b)\) before the call, after the call it represents \((\textit{op} \circ \textit{ops})(s_b)\). Ifchanged
is notundefined
,changed.value
must at exit have a value that represents the set of indices whose selection state changed (fromtrue
tofalse
or vice versa). Ifchanged.value
is defined when entering the function, the indices it represents are considered to be the indices changed by a preceding call topush
orpop
, and the joint effect is tracked. If \(J_p\) are those indices and \(J_c\) the indices changed by the currentpush
operation, then the resultingchanged.value
is \(J_c \setminus J_p\). Howchanged.value
is represented is up to the selection storage.The
push
method may ignorechanged
altogether, ifstorage
is never used in a selection state with change tracking on.
storage.pop(changed)
- Precondition:
storage.size() >= 1
. - Removes a primitive selection operation from the front of the
op-composition. If
storage
represents \((\textit{op} \circ \textit{ops})(s_b)\) before the call, after the the call it represents \(\textit{ops}(s_b)\). The meaning and requirements for thechanged
parameter are the same as in thepush
function. - Returns the removed primitive selection operation.
storage.top()
- Precondition:
storage.size() >= 1
. - Returns a reference to the first (most recently pushed) primitive
selection operation. That is, if
storage
represents \(\textit{op} \circ \textit{ops}(s_b)\), returns \(\textit{op}\).
storage.top2()
- Precondition:
storage.size() >= 2
. - Returns a reference to the second (second most recently pushed)
primitive selection operation. That is, if
storage
represents \(\textit{op}_a \circ \textit{op}_b \circ \textit{ops}(s_b)\), returns \(\textit{op}_b\).
storage.size()
- Returns the number of primitive selection operations in the composition represented by \(\textit{ops}\). (If \(\textit{ops}\) is empty, all of the selection state is represented in the base selection mapping \(s_b\) portion of \(\textit{ops}(s_b)\).)
storage.bake()
- Removes one primitive selection operation (the least recently
pushed), applies it to the base selection mapping, and makes the
result the new base. That is, if
storage
represents \((\textit{ops} \circ \textit{op})(s_b)\), it is modified to represent \(\textit{ops}(s'_b)\), where \(s'_b = \textit{op}(s_b))\). The function has no effect whenstorage.size() == 0
.
storage.onSelected(J)
- Returns
true
if the selection domainJ
is considered to indicate a selected element,false
otherwise. A typical implementation would returnstorage.at(i)
ifi
is the only element inJ
, otherwisefalse
.
storage.modifyStorage(cmd)
- The
cmd
parameter is a command that indicates howstorage
should be modified. What commands are accepted and what their effects are is defined by the client. Example functionality to provide through this method include reacting to removing indices, adding indices, and reordering indices (if they are stored in a data structure where ordering matters).
storage.equalDomains(J1, J2)
- Returns
true
ifJ1
andJ2
are equivalent sets of indices.
storage.isEmpty(J)
- Returns
true
ifJ
is an empty set of indices.
2.4 Helper definitions
2.4.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.
2.4.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.4.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 OS X 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.