Library for manipulating text ranges and selections, and assorted other programs that use that
bililiteRange dataOften, we want to keep data that is specific to a given element, and shared by all ranges on that element.
Simply setting element.foo = bar works, but in the olden days (Internet Explorer 6) that
caused memory leaks. That is no longer a problem, but
putting so many new fields on the element lacks a certain elegance. So we create a single field, element.bililiteRangeData,
and store all our fields in that, with element.bililiteRangeData.foo = bar.
To avoid any possibility of a name conflict, we use a private variable const dataKey = Symbol() and actually do
element[dataKey].foo. To expose that (since dataKey is not exposed)
we define for each range range.data = range.element[dataKey].
So, for example,
let range = bililiteRange( document.querySelector('div.editor') );
range.data.hilighter = 'Prism';
// elsewhere in the code
let range2 = bililiteRange( document.querySelector('div.editor') );
console.log(range2.data.hilighter); // 'Prism'
That works well, but I created bililiteRange in part to implement the ex line editor, and I want some data to have default
values that can be changed for each element. So data is actually an object with a prototype.
bililiteRange.createOption(prop, descriptor) does Object.defineProperty(data.prototype, prop, descriptor). Now every range has a
property prop with an
object descriptor.
So to set autoindent, do bililiteRange.createOption('autoindent', {value: false}).
The difference with Object.defineProperty is that the defaults are {
enumerable: true,
writable: true,
configurable: true
} rather than false.
There is one more option that can be added to the property descriptor: monitored. bililiteRange.createOption(prop, {monitored: true})
adds a set handler for prop that, whenever prop is set (as in range.data[prop] = newValue), that change is signaled in two ways:
range.dispatch(CustomEvent(`data-${prop}`, {detail: newValue})) for use with EventListeners.
range.element.setAttribute(`data-${prop}`, newValue) for use with MutationObservers. Note that attributes have very limited legal
characters, so this will silently fail if data-${prop} is not a legal attribute name.
data.triggerThe function range.data.trigger() will trigger the CustomEvent and setAttribute for all monitored options. This is useful for initializing
things after defining the data, without setting options to differ from their defaults. Call this after all your bililiteRange.createOptions
(the program can’t call this automatically since there’s no way to know when the user is done).
bililiteRange.prototype.ex uses
JSON.stringify to display
options. The data object, therefore has a
toJSON method
that only returns data fields that were defined with bililiteRange.createOption and are defined on this particular element.
Thus,
bililiteRange.createOption('foo', {value: 1}); // data created for all bililiteRange's
bililiteRange.createOption('bar', {value: 2});
const range = bililiteRange(element);
range.data.baz = 3; // data created for this bililiteRange's element only
range.data.foo = 7; // modify an option created above.
console.log( JSON.stringify(range.data) ); // {"foo":7}
console.log( JSON.stringify(range.data.all) ); // {"foo":7,"bar":2}
If you really want to stringify all of range.data, use JSON.stringify( Object.assign({}, range.data)) to copy it.
The following are data fields used by this package:
bililiteRange.js: mousetime, liveRanges, oldText, selection, sendkeysBounds, sendkeysOriginalText
bililiteRange.undo.js: undos
bililiteRange.lines.js: autoindent, tabsize
bililiteRange.find.js: bigwords, dotall, global, ignorecase, magic, multiline, sections,
sentences, unicode, words, wrapscan
bililiteRange.ex.js: directory, file, reader, stderr, stderr, savestatus, writer