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
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 pageIdstring Id of a registered page
containerHTMLElement Element to draw into
querystring <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
entrynames. 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 keystring 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: falseParameters:
Name Type Description keystring 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 keystring 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
importchecks every value anywayParameters:
Name Type Attributes Description optsObject <optional>
Properties
Name Type Attributes Default Description syncboolean <optional>
false Leaves out options declared with
sync: false, for sending to other devicesReturns:
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
registerreplaces a pageParameters:
Name Type Description pageIdstring Id of the page to extend
extensionIdstring Unique id of the section, usually the id of the plugin adding it
sectionObject Section declaration
Properties
Name Type Attributes Description titlestring <optional>
Heading of the section. Defaults to the extension id
namespacestring <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
blocksArray.<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
repeatholds its valid stored entries, and the default entry of each itemoverreturns that has none stored;overmust be able to list the items by thenParameters:
Name Type Attributes Description keystring 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 throughset, so it is validated and announced like any other change. Keys no registered page declares are skipped, and so are options declared withsync: falsewhen the values come from another device (sourceremote)Parameters:
Name Type Attributes Description valuesObject.<string, *> Values by option key
optsObject <optional>
Properties
Name Type Attributes Default Description sourcestring <optional>
'local' Origin of the values, passed to the optionChanged hook; sync uses
remoteReturns:
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.addButtonis 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, unlessiitc.optionsPaneis offParameters:
Name Type Attributes Description pageIdstring <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 pageIdstring <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 pageIdstring Unique page id, usually the plugin id
pageIITC.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 keystring Option key,
<namespace>.<key>optsObject <optional>
Properties
Name Type Attributes Default Description sourcestring <optional>
'local' Origin of the change, passed to the optionChanged hook; sync uses
remoteReturns:
boolean -False if a
remotereset targets an undeclared key or an option declared withsync: falseExample
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
resetdoes. A map of arepeatkeeps only the entries that differ from the default of their item, and is removed when none is left. TheoptionChangedhook and the block'sonChangerun when the value actually changes, and receive the value asgetreturns itStoring
undefinedunder an undeclared key removes the value, asresetdoes; a declared option rejects itParameters:
Name Type Attributes Description keystring Option key,
<namespace>.<key>value* New value
optsObject <optional>
Properties
Name Type Attributes Default Description sourcestring <optional>
'local' Origin of the change, passed to the optionChanged hook; sync uses
remote, andlegacymarks a value moved from an older build: it gets no sync time, so the shared records win over itReturns:
boolean -False if the value is not allowed or could not be stored, or if a
remotevalue targets an undeclared key or an option declared withsync: falseExamples
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
repeatblocks, and skips blocks of unknown types with a warningParameters:
Name Type Attributes Description namespacestring Prefix of the page's keys
blocksArray.<IITC.options.OptionBlock> Blocks to scan
repeatIITC.options.OptionBlock <optional>
The
repeatblock the blocks are repeated in, so their options hold mapsReturns:
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 pageIdstring Page id
extensionIdstring | undefined Extension id, undefined for the page itself
namespacestring Prefix of the keys
blocksArray.<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 keystring Full option key
stored* Value read from localStorage, undefined when there is none
quietboolean <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
typetells what it draws and which properties it reads:typeDraws Reads boolean,number,string,select,colora field holding the option its keynameskey,title,description,default,value,validate,onChange,sync;min,max,stepfornumber;choicesforselect;alphaforcoloractiona button that runs actiontitle,description,button,icon,actionexportCopy, and Save file with a filenametitle,description,data,filename,dataTypeimportPaste, and Choose file with accepttitle,description,onData,accept,onFileschoicea radio before the title of each item of a repeat, one choice perrepeatselected,actionheading,linka section title; a link title,urltext,noticea paragraph; a framed message text,levellistentries, nested ones folded itemsrepeata table with a row of blocksfor every item ofoverover,blocksEvery block reads
visible, and fields, actions, exports, imports and choices alsoenabled. 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 arepeatthe function receives the itemProperties:
Name Type Attributes Default Description typestring Block type, from the table above
titlestring | function <optional>
Label of a field, an action, an
exportor animport, text of aheadingor alinkbuttonstring | function <optional>
Short text of the button of an
action, such as 'Delete', next to thetitle. Without it the button carries thetitleiconstring | function <optional>
Icon an
actionbutton shows instead of its text, a Material Symbols name such as 'delete'; the text becomes the tooltipdescriptionstring | function <optional>
Explanation shown under the title
visibleboolean | function <optional>
true False hides the block
enabledboolean | function <optional>
true False shows the block without letting the user change or run it
keystring <optional>
Name of the option a field holds, addressed as
<namespace>.<key>; every field has onedefault* | function <optional>
Value used while nothing valid is stored. Inside a
repeatit may be a function of the item, giving the default of the item's entryvalue* | 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)
minnumber <optional>
Lowest allowed
numbermaxnumber <optional>
Highest allowed
numberstepnumber <optional>
Allowed
numberincrement, counted fromminor 0choicesArray | function <optional>
selectchoices: values or{value, label}objects. A function must return all choices at once: a value missing from them is not allowedalphaboolean <optional>
Allows
colorvalues with an alpha channel (#rrggbbaa)validatefunction <optional>
Returns true, or an error message for a value that is not allowed
onChangefunction <optional>
Called with
(value, oldValue, source)after the value has changedsyncboolean <optional>
true False keeps the option on this device: sync neither sends nor applies it, while a local backup still holds it
actionfunction <optional>
Runs when an
actionblock is pressed, or achoiceis made, with the item inside arepeat; the text it returns and the error it throws are shown next to the button, and a promise is waited forselectedboolean | function <optional>
Whether the item is the chosen one, for a
choicedatafunction <optional>
Returns the text an
exportblock hands over; receives the item inside arepeatfilenamestring <optional>
Lets an
exportblock also savedataas a file of this namedataTypestring <optional>
MIME type of that file
onDatafunction <optional>
Receives
(text, filename, item)from animportblock: 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 foracceptstring <optional>
Lets an
importblock also read files of these types, as in theacceptattribute of a file inputonFilesfunction <optional>
Receives
(files, item)from animportblock withacceptinstead ofonData, and lets the user choose several files: all of them, as[{name, text}]; what it returns and throws is shown as foronData. With it,onDatais needed only for the Paste buttontextstring | function <optional>
Text of a
textor anoticeblockurlstring | function <optional>
Address a
linkopenslevel'info' | 'warning' | 'error' | function <optional>
'info' Kind of a
noticeitemsArray.<IITC.options.OptionsListItem> | function <optional>
Entries of a
listoverfunction <optional>
Items of a
repeatblock, returned as[{id, title}]with ids that stay the same between calls;getalso calls it to give the items without a stored entry their default, so it must be able to list them whenever the options are readblocksArray.<IITC.options.OptionBlock> <optional>
Blocks of a
repeatblock; their options hold a map of item id to value -
OptionsListItem
-
Properties:
Name Type Attributes Default Description titlestring Main text
substring <optional>
Second line
badgestring <optional>
Short text at the end of the line, such as a version
tooltipstring <optional>
Text shown on hover over the main line
state'muted' | 'error' <optional>
Shows the entry as secondary, or as failed
itemsArray.<IITC.options.OptionsListItem> <optional>
Nested entries
collapsedboolean <optional>
false Hides the nested entries until the entry is opened
-
OptionsPage
-
Properties:
Name Type Attributes Description titlestring Page title
namespacestring <optional>
Prefix of the page's option keys. Defaults to the page id
blocksArray.<IITC.options.OptionBlock> Page content
shortcutboolean | Object | function <optional>
Adds a toolbox button that opens the page, while the
iitc.toolboxShortcutsoption is on:true, or the arguments ofIITC.toolbox.addButtonbutidandaction(labelis the page title by default), or a function returning them, called again onIITC.options.refresh(pageId), for a button that shows stateaccesskeystring <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 pageIdstring Page drawn last
messagesMap.<string, Object> Texts left by actions of that page, by control id
pendingSet.<string> Actions still running, as
<pageId>#<control id>scheduledboolean A redraw is waiting
pointerDownboolean A mouse button or a finger is down in the container
afterPointerfunction <optional>
The redraw waiting for the pointer to come up