# bililiteRange

Library for manipulating text ranges and selections, and assorted other programs that use that

View the Project on GitHub dwachss/bililiteRange

# bililiteRange `bounds`

Setting the bounds with `range.bounds([x,y])` does no error checking or constraining, since the text may change. The constraining happens when the bounds are gotten with `range.bounds()`. Then both numbers are constrained with `Math.max(0, Math.min (this.length, x or y))`, and the second number is constrained to be greater or equal to the first, with `y = Math.max(x, y)`.

## The “built-in” functions

### `bounds('all')`

Sets the range to cover the entire element: `[0, range.length]`.

### `bounds('end')`

Sets the range to a zero-length range at the end of the element: `[range.length, range.length]`.

### `bounds('endbounds')`

Sets the range to a zero-length range at the end of the current range: `[range, range]`.

### `bounds('intersection',...rest)`

Creates a new range with `range.clone().bounds(...rest)`, then sets the bounds of this range to the insersection of the two ranges. Thus `range.bounds([2,4]).bounds('intersection', [3,5])` leaves `range.bounds()` as `[3,4]`. If the intersection is the null set, then the range is set to an empty range at an undefined location (with the current algorithm, it is the start of the later bounds, but I may change that).

### `bounds('selection')`

Sets the range to the selected part of the element. This, along with `range.select()`, is how to interact with the user. `bililiteRange`’s act like selections, but they are not visible in the user interface.

### `bounds('start')`

Sets the range to a zero-length range at the start of the element: `[0, 0]`.

### `bounds('startbounds')`

Sets the range to a zero-length range at the start of the current range: `[range, range]`.

## Custom functions

`bililiteRange.prototype.bounds(s: string)` looks for a function `bililiteRange.bounds[s]` and calls that with `b = bililiteRange.bounds[s].apply(this, arguments)`, then sets the bounds with `range.bounds(b)`.

So `range.bounds('all')` calls `bililiteRange.bounds.all = () => [0, this.length];`.

Extending bounds is easy, just do:

``````bililiteRange.bounds.firstchar = () => [0,1];

range.all('ABCDE').bounds('firstchar').text(); // 'A'
``````

Error checking in the sense of constraining the bounds to fit the text (in the `firstchar` example, what if there is no text?) is not necessary. `range.bounds()` will constrain the actual bounds used so `bounds <= bounds <= length`.

The arguments are passed to the function, so extended arguments are possible:

``````bililiteRange.bounds.nthchar = (name, n) => [+n, n+1];

range.all('ABCDE').bounds('nthchar', 2).text(); // 'C'
``````

And you can even manipulate the text, since the range is passed in as `this`, but this is a bad idea.

``````bililiteRange.bounds.wrap = function (name, before, after) {
return this.text(before + this.text() + after, {select: 'all'}).bounds();
}

range.all('ABCDE').bounds('firstchar').bounds('wrap', 'foo', 'bar').text(); // 'fooAbar'
``````

A more realistic use of `this` is to go to the end of the line (after next following newline):

``````bililiteRange.bounds.EOL = function () {
if (this.text()[this.length-1] == '\n') return [this, this]; // range ends with a newline
const nextnewline = this.all().indexOf('\n', this);
if (nextnewline != -1) return [nextnewline + 1, nextnewline +1];
return this.bounds('end').bounds(); // no newline
}

range.all('Hello\nWorld').bounds('start').bounds('EOL').text('Wonderful ').all(); // 'Hello\nWonderful World'
``````