IITC.options

Options API

A plugin declares its options page with register in its setup, reads the values with get where it needs them and reacts to changes in onChange. The options are blocks with a key; the core validates every value, applies the defaults and stores only values that differ from the default. Blocks without a key are the rest of the page: actions and content. Options sync between devices by default, through the Sync plugin; sync: false keeps one on this device. A plugin without a page of its own adds a section to the page of another with extend

Every value is kept under iitc-option:<key>. Keys that were never declared still work through get/set, without validation; until a page or an extension of one declares it, a key is read and written as undeclared

Classes

PageView

Methods

private, static _pageIds() → {Array.<string>}

Ids of the registered pages in the order the window lists them

Returns:
Array.<string>

private, static _renderPage(pageId, container, queryopt) → {boolean}

Draws an options page into a container, replacing what the container holds. Changes made on the page draw it again; a change made elsewhere needs another call

Parameters:
Name Type Attributes Description
pageId string

Id of a registered page

container HTMLElement

Element to draw into

query string <optional>

Search whose matches on the page are marked; stays in force for the redraws until another is given

Returns:
boolean -

False when no such page is registered

private, static _setupWindow()

Shows the way to the window that entry names. An app cannot remove a pane, so it stays until IITC is reloaded and still opens the window

private, static _storedState(key) → {'missing'|'valid'|'invalid'}

Tells what localStorage holds for a key, without logging: nothing, a value that reads and passes the declaration, or one that cannot be read or fails it (an old range, choices not loaded yet)

Parameters:
Name Type Description
key string

Option key, <namespace>.<key>

Returns:
'missing' | 'valid' | 'invalid' -

The state of the stored value

private, static _syncs(key) → {boolean}

Tells whether an option may travel to other devices: it is declared, and not with sync: false

Parameters:
Name Type Description
key string

Option key, <namespace>.<key>

Returns:
boolean -

True for a declared option that syncs

static check(key, value) → {true|string}

Tells whether an option may take a value, without storing it; an undeclared key takes any value

Parameters:
Name Type Description
key string

Option key, <namespace>.<key>

value *

Value to check

Returns:
true | string -

True, or the reason the value is not allowed

static exportValues(optsopt) → {Object.<string, *>}

Collects the stored values of declared options; defaults are left out, so an import on another device keeps that device's defaults. Values are not validated here: choices may still be loading, and import checks every value anyway

Parameters:
Name Type Attributes Description
opts Object <optional>
Properties
Name Type Attributes Default Description
sync boolean <optional>
false

Leaves out options declared with sync: false, for sending to other devices

Returns:
Object.<string, *> -

Values by option key

Example
const backup = JSON.stringify(IITC.options.exportValues());

static extend(pageId, extensionId, section) → {boolean}

Adds a section to the page of another plugin, such as options of an add-on on the page of the plugin it works with. The section follows the page's own blocks under a heading of its title; sections of several extensions follow by title, whatever order their plugins load in. Its options are declared at once, whether the page is registered yet or not, and the section shows once it is. Extending the page again with the same extension id replaces the section and its options, as register replaces a page

Parameters:
Name Type Description
pageId string

Id of the page to extend

extensionId string

Unique id of the section, usually the id of the plugin adding it

section Object

Section declaration

Properties
Name Type Attributes Description
title string <optional>

Heading of the section. Defaults to the extension id

namespace string <optional>

Prefix of the section's option keys. Defaults to the extension id, so a plugin that also registers a page of its own under its id gives the section a namespace of its own: a key declared twice stays with the first declaration

blocks Array.<IITC.options.OptionBlock>

Section content, of the same blocks as a page

Returns:
boolean -

True if the section was added

Examples
IITC.options.extend('draw-tools', 'kml-import', {
  title: 'KML import',
  blocks: [
    { type: 'boolean', key: 'fit', title: 'Zoom to the imported shapes', default: true },
    { type: 'import', title: 'KML file', onData: (text) => importKml(text), accept: '.kml' },
  ],
});
if (IITC.options.get('kml-import.fit')) map.fitBounds(bounds);

A key on the API keys page

// the section is titled after the service, and its key reads as `api-keys.<service>`
IITC.options.extend('api-keys', 'my-plugin', {
  title: 'Example Maps',
  namespace: 'api-keys',
  blocks: [
    {
      type: 'string',
      key: 'example-maps',
      title: 'API key',
      description: 'Empty uses the key IITC comes with',
      onChange: () => layer.setUrl(tileUrl()),
    },
    { type: 'link', title: 'Get a key', url: 'https://example.com/keys' },
  ],
});
const tileUrl = () => `https://tiles.example.com/{z}/{x}/{y}.png?key=${IITC.options.get('api-keys.example-maps').trim() || SHARED_KEY}`;

static get(key, fallbackopt) → {*}

Returns the value of an option; a declared option falls back to its default when nothing valid is stored. A map of a repeat holds its valid stored entries, and the default entry of each item over returns that has none stored; over must be able to list the items by then

Parameters:
Name Type Attributes Description
key string

Option key, <namespace>.<key>

fallback * <optional>

Value for an undeclared key with nothing stored

Returns:
* -

The option value

Example
const mode = IITC.options.get('link-show-direction.mode');

static importValues(values, optsopt) → {Object}

Stores values produced by export. Each value goes through set, so it is validated and announced like any other change. Keys no registered page declares are skipped, and so are options declared with sync: false when the values come from another device (source remote)

Parameters:
Name Type Attributes Description
values Object.<string, *>

Values by option key

opts Object <optional>
Properties
Name Type Attributes Default Description
source string <optional>
'local'

Origin of the values, passed to the optionChanged hook; sync uses remote

Returns:
Object -

Keys stored, keys whose value was not allowed or could not be stored, and keys left alone

Example
const { rejected } = IITC.options.importValues(JSON.parse(backup));

static open(pageIdopt) → {boolean}

Opens the options window, on a given page or on the page chosen last

A toolbox button to an options page comes from the page's shortcut, which the user can turn off with the other shortcuts; IITC.toolbox.addButton is for a plugin's own dialogs that are not part of the options, such as lists and other views of map data. In an app with panes the window is a pane of the app, unless iitc.optionsPane is off

Parameters:
Name Type Attributes Description
pageId string <optional>

Page to show

Returns:
boolean -

False when no such page is registered

Example
// a toolbox button that opens the page
IITC.options.register('my-plugin', { title: 'My plugin', shortcut: true, blocks });
// the page opened from the plugin's own code, such as a link in its dialog
IITC.options.open('my-plugin');

static refresh(pageIdopt)

Draws the page shown in the options window again, after the current task and after the click when the pointer is down, and updates the toolbox shortcut of the page. Changes of the page's own options and of the sections extensions add to it redraw it already; a page that also shows other state calls this when that state changes

Parameters:
Name Type Attributes Description
pageId string <optional>

Redraws only if this page is shown; all shortcuts are updated without it

static register(pageId, page) → {boolean}

Declares an options page and the options its blocks hold. Registering a page id again replaces the earlier declaration and announces no change: a stored value the new declaration rejects reads as the default

Parameters:
Name Type Description
pageId string

Unique page id, usually the plugin id

page IITC.options.OptionsPage

Page declaration

Returns:
boolean -

True if the page was registered

Examples

A page

IITC.options.register('my-plugin', {
  title: 'My plugin',
  shortcut: true,
  blocks: [
    { type: 'boolean', key: 'show', title: 'Show on the map', default: true, onChange: (show) => (show ? draw() : clear()) },
    { type: 'select', key: 'style', title: 'Style', choices: ['Lines', 'Dots'], default: 'Lines', onChange: draw },
    { type: 'heading', title: 'Data' },
    { type: 'export', title: 'Marked portals', data: () => JSON.stringify(load()), filename: 'my-plugin.json' },
    { type: 'import', title: 'Marked portals', onData: (text) => save(JSON.parse(text)), accept: '.json' },
    {
      type: 'action',
      title: 'Forget marked portals',
      button: 'Forget',
      // a promise is waited for; the text returned or the error thrown shows next to the button
      action: async () => {
        if (!Object.keys(load()).length) throw new Error('Nothing is marked');
        await save({});
        return 'Forgotten';
      },
    },
  ],
});
// the values are read where they are needed
if (IITC.options.get('my-plugin.show')) draw();

An option for each item of a list

IITC.options.register('my-plugin', {
  title: 'My plugin',
  blocks: [
    {
      type: 'repeat',
      over: () => [
        { id: 'ENL', title: 'Enlightened' },
        { id: 'RES', title: 'Resistance' },
      ],
      blocks: [{ type: 'color', key: 'color', title: 'Color', default: (item) => (item.id === 'ENL' ? '#03dc03' : '#0088ff') }],
    },
  ],
});
// a map of item id to value, with the default of every item that has nothing stored
IITC.options.get('my-plugin.color'); // { ENL: '#03dc03', RES: '#0088ff' }

static reset(key, optsopt) → {boolean}

Removes the stored value, so a declared option returns to its default

Parameters:
Name Type Attributes Description
key string

Option key, <namespace>.<key>

opts Object <optional>
Properties
Name Type Attributes Default Description
source string <optional>
'local'

Origin of the change, passed to the optionChanged hook; sync uses remote

Returns:
boolean -

False if a remote reset targets an undeclared key or an option declared with sync: false

Example
IITC.options.reset('draw-tools.color');

static set(key, value, optsopt) → {boolean}

Stores the value of an option. Values of declared options are validated first; a value stored in the same form as the one already there changes nothing, and a value stored in the same form as the default is removed, as reset does. A map of a repeat keeps only the entries that differ from the default of their item, and is removed when none is left. The optionChanged hook and the block's onChange run when the value actually changes, and receive the value as get returns it

Storing undefined under an undeclared key removes the value, as reset does; a declared option rejects it

Parameters:
Name Type Attributes Description
key string

Option key, <namespace>.<key>

value *

New value

opts Object <optional>
Properties
Name Type Attributes Default Description
source string <optional>
'local'

Origin of the change, passed to the optionChanged hook; sync uses remote, and legacy marks a value moved from an older build: it gets no sync time, so the shared records win over it

Returns:
boolean -

False if the value is not allowed or could not be stored, or if a remote value targets an undeclared key or an option declared with sync: false

Examples
IITC.options.set('draw-tools.color', '#a24ac3');

Moving an option from an older localStorage key

IITC.options.register('my-plugin', {
  title: 'My plugin',
  blocks: [{ type: 'number', key: 'radius', title: 'Radius, m', min: 0, default: 40 }],
});
const legacy = localStorage.getItem('plugin-my-plugin-radius');
if (legacy !== null) {
  // a legacy value gets no sync time, so a value synced from other devices wins over it
  IITC.options.set('my-plugin.radius', Number(legacy), { source: 'legacy' });
  // the value moves once
  localStorage.removeItem('plugin-my-plugin-radius');
}

private, inner collectDefinitions(namespace, blocks, repeatopt) → {Array.<Object>}

Collects the options declared by a page, including the ones inside repeat blocks, and skips blocks of unknown types with a warning

Parameters:
Name Type Attributes Description
namespace string

Prefix of the page's keys

blocks Array.<IITC.options.OptionBlock>

Blocks to scan

repeat IITC.options.OptionBlock <optional>

The repeat block the blocks are repeated in, so their options hold maps

Returns:
Array.<Object> -

Option definitions

private, inner declare(pageId, extensionId, namespace, blocks) → {Array.<string>}

Declares the options of a page or of an extension of it in place of the ones that declaration made before; a key declared elsewhere already stays with that declaration, with a warning

Parameters:
Name Type Description
pageId string

Page id

extensionId string | undefined

Extension id, undefined for the page itself

namespace string

Prefix of the keys

blocks Array.<IITC.options.OptionBlock>

Blocks holding the options

Returns:
Array.<string> -

The keys declared

private, inner resolve(key, stored, quietopt) → {*}

Turns the stored value of a declared option into its effective value

Parameters:
Name Type Attributes Default Description
key string

Full option key

stored *

Value read from localStorage, undefined when there is none

quiet boolean <optional>
false

Skips the invalid-value warning

Returns:
* -

The stored value if it is valid, otherwise the default; of a stored map, the valid entries

private, inner search() → {Array.<string>|null}

Finds the texts of a page that contain the query, ignoring case

Returns:
Array.<string> | null -

Texts of the page's blocks that match, empty when only the page title does; null when nothing on the page matches

private, inner validateValue() → {true|string}

Checks one value against an option's type and constraints

Returns:
true | string -

True, or the reason the value is not allowed

Type Definitions

OptionBlock

A block of an options page; its type tells what it draws and which properties it reads:

type Draws Reads
boolean, number, string, select, color a field holding the option its key names key, title, description, default, value, validate, onChange, sync; min, max, step for number; choices for select; alpha for color
action a button that runs action title, description, button, icon, action
export Copy, and Save file with a filename title, description, data, filename, dataType
import Paste, and Choose file with accept title, description, onData, accept, onFiles
choice a radio before the title of each item of a repeat, one choice per repeat selected, action
heading, link a section title; a link title, url
text, notice a paragraph; a framed message text, level
list entries, nested ones folded items
repeat a table with a row of blocks for every item of over over, blocks

Every block reads visible, and fields, actions, exports, imports and choices also enabled. Display properties (title, description, button, icon, text, url, level, items, visible, enabled, value, selected) take a value or a function, called whenever the page is drawn; inside a repeat the function receives the item

Properties:
Name Type Attributes Default Description
type string

Block type, from the table above

title string | function <optional>

Label of a field, an action, an export or an import, text of a heading or a link

button string | function <optional>

Short text of the button of an action, such as 'Delete', next to the title. Without it the button carries the title

icon string | function <optional>

Icon an action button shows instead of its text, a Material Symbols name such as 'delete'; the text becomes the tooltip

description string | function <optional>

Explanation shown under the title

visible boolean | function <optional>
true

False hides the block

enabled boolean | function <optional>
true

False shows the block without letting the user change or run it

key string <optional>

Name of the option a field holds, addressed as <namespace>.<key>; every field has one

default * | function <optional>

Value used while nothing valid is stored. Inside a repeat it may be a function of the item, giving the default of the item's entry

value * | function <optional>

Value a field shows instead of the stored one, when it is derived from other options (a flag that a broader rule already sets)

min number <optional>

Lowest allowed number

max number <optional>

Highest allowed number

step number <optional>

Allowed number increment, counted from min or 0

choices Array | function <optional>

select choices: values or {value, label} objects. A function must return all choices at once: a value missing from them is not allowed

alpha boolean <optional>

Allows color values with an alpha channel (#rrggbbaa)

validate function <optional>

Returns true, or an error message for a value that is not allowed

onChange function <optional>

Called with (value, oldValue, source) after the value has changed

sync boolean <optional>
true

False keeps the option on this device: sync neither sends nor applies it, while a local backup still holds it

action function <optional>

Runs when an action block is pressed, or a choice is made, with the item inside a repeat; the text it returns and the error it throws are shown next to the button, and a promise is waited for

selected boolean | function <optional>

Whether the item is the chosen one, for a choice

data function <optional>

Returns the text an export block hands over; receives the item inside a repeat

filename string <optional>

Lets an export block also save data as a file of this name

dataType string <optional>

MIME type of that file

onData function <optional>

Receives (text, filename, item) from an import block: the pasted text, or the file read; the text it returns is shown instead of 'Imported', an error it throws is shown too, and a promise is waited for

accept string <optional>

Lets an import block also read files of these types, as in the accept attribute of a file input

onFiles function <optional>

Receives (files, item) from an import block with accept instead of onData, and lets the user choose several files: all of them, as [{name, text}]; what it returns and throws is shown as for onData. With it, onData is needed only for the Paste button

text string | function <optional>

Text of a text or a notice block

url string | function <optional>

Address a link opens

level 'info' | 'warning' | 'error' | function <optional>
'info'

Kind of a notice

items Array.<IITC.options.OptionsListItem> | function <optional>

Entries of a list

over function <optional>

Items of a repeat block, returned as [{id, title}] with ids that stay the same between calls; get also calls it to give the items without a stored entry their default, so it must be able to list them whenever the options are read

blocks Array.<IITC.options.OptionBlock> <optional>

Blocks of a repeat block; their options hold a map of item id to value

OptionsListItem

Properties:
Name Type Attributes Default Description
title string

Main text

sub string <optional>

Second line

badge string <optional>

Short text at the end of the line, such as a version

tooltip string <optional>

Text shown on hover over the main line

state 'muted' | 'error' <optional>

Shows the entry as secondary, or as failed

items Array.<IITC.options.OptionsListItem> <optional>

Nested entries

collapsed boolean <optional>
false

Hides the nested entries until the entry is opened

OptionsPage

Properties:
Name Type Attributes Description
title string

Page title

namespace string <optional>

Prefix of the page's option keys. Defaults to the page id

blocks Array.<IITC.options.OptionBlock>

Page content

shortcut boolean | Object | function <optional>

Adds a toolbox button that opens the page, while the iitc.toolboxShortcuts option is on: true, or the arguments of IITC.toolbox.addButton but id and action (label is the page title by default), or a function returning them, called again on IITC.options.refresh(pageId), for a button that shows state

accesskey string <optional>

Access key that opens the page, with the toolbox button or without it; the button shows it in its tooltip

ContainerState

What a container remembers between drawings

Properties:
Name Type Attributes Description
pageId string

Page drawn last

messages Map.<string, Object>

Texts left by actions of that page, by control id

pending Set.<string>

Actions still running, as <pageId>#<control id>

scheduled boolean

A redraw is waiting

pointerDown boolean

A mouse button or a finger is down in the container

afterPointer function <optional>

The redraw waiting for the pointer to come up