@teselagen/range-utils 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/index.js +6542 -18010
  2. package/index.mjs +6593 -18007
  3. package/index.umd.js +6836 -18306
  4. package/package.json +3 -2
  5. package/src/adjustRangeToDeletionOfAnotherRange.js +53 -0
  6. package/src/adjustRangeToDeletionOfAnotherRange.test.js +107 -0
  7. package/src/adjustRangeToInsert.js +28 -0
  8. package/src/adjustRangeToInsert.test.js +111 -0
  9. package/src/adjustRangeToRotation.js +24 -0
  10. package/src/adjustRangeToRotation.test.js +123 -0
  11. package/src/checkIfNonCircularRangesOverlap.js +30 -0
  12. package/src/checkIfNonCircularRangesOverlap.test.js +45 -0
  13. package/src/checkIfPotentiallyCircularRangesOverlap.js +16 -0
  14. package/src/checkIfPotentiallyCircularRangesOverlap.test.js +73 -0
  15. package/src/collapseOverlapsGeneratedFromRangeComparisonIfPossible.js +47 -0
  16. package/src/collapseOverlapsGeneratedFromRangeComparisonIfPossible.test.js +124 -0
  17. package/src/convertRangeIndices.js +22 -0
  18. package/src/convertRangeIndices.test.js +22 -0
  19. package/src/convertRangeTo0Based.js +5 -0
  20. package/src/convertRangeTo1Based.js +5 -0
  21. package/src/doesRangeSpanEntireSequence.js +7 -0
  22. package/src/doesRangeSpanOrigin.js +3 -0
  23. package/src/expandOrContractCircularRangeToPosition.js +41 -0
  24. package/src/expandOrContractNonCircularRangeToPosition.js +25 -0
  25. package/src/expandOrContractRangeByLength.js +12 -0
  26. package/src/expandOrContractRangeByLength.test.js +77 -0
  27. package/src/expandOrContractRangeToPosition.js +10 -0
  28. package/src/flipContainedRange.js +85 -0
  29. package/src/flipContainedRange.test.js +124 -0
  30. package/src/generateRandomRange.js +20 -0
  31. package/src/generateRandomRange.test.js +24 -0
  32. package/src/getAnnotationRangeType.js +24 -0
  33. package/src/getAnnotationRangeType.test.js +59 -0
  34. package/src/getEachPositionInRangeAsArray.js +15 -0
  35. package/src/getEachPositionInRangeAsArray.test.js +9 -0
  36. package/src/getLengthOfOverlappingRegionsBetweenTwoRanges.js +9 -0
  37. package/src/getLengthOfOverlappingRegionsBetweenTwoRanges.test.js +24 -0
  38. package/src/getMiddleOfRange.js +10 -0
  39. package/src/getMiddleOfRange.test.js +44 -0
  40. package/src/getOverlapOfNonCircularRanges.js +35 -0
  41. package/src/getOverlapsOfPotentiallyCircularRanges.js +54 -0
  42. package/src/getOverlapsOfPotentiallyCircularRanges.test.js +199 -0
  43. package/src/getPositionFromAngle.js +7 -0
  44. package/src/getRangeAngles.js +33 -0
  45. package/src/getRangeAngles.test.js +77 -0
  46. package/src/getRangeLength.js +14 -0
  47. package/src/getRangeLength.test.js +30 -0
  48. package/src/getRangesBetweenTwoRanges.js +28 -0
  49. package/src/getSequenceWithinRange.js +17 -0
  50. package/src/getSequenceWithinRange.test.js +49 -0
  51. package/src/getShortestDistanceBetweenTwoPositions.js +12 -0
  52. package/src/getShortestDistanceBetweenTwoPositions.test.js +14 -0
  53. package/src/getYOffsetForPotentiallyCircularRange.js +24 -0
  54. package/src/getYOffsetsForPotentiallyCircularRanges.js +20 -0
  55. package/src/getYOffsetsForPotentiallyCircularRanges.test.js +29 -0
  56. package/src/getZeroedRangeOverlaps.js +17 -0
  57. package/src/getZeroedRangeOverlaps.test.js +36 -0
  58. package/src/index.js +51 -0
  59. package/src/index.test.js +33 -0
  60. package/src/invertRange.js +21 -0
  61. package/src/invertRange.test.js +96 -0
  62. package/src/isPositionCloserToRangeStartThanRangeEnd.js +8 -0
  63. package/src/isPositionCloserToRangeStartThanRangeEnd.test.js +17 -0
  64. package/src/isPositionWithinRange.js +31 -0
  65. package/src/isRangeOrPositionWithinRange.js +29 -0
  66. package/src/isRangeOrPositionWithinRange.test.js +150 -0
  67. package/src/isRangeWithinRange.js +17 -0
  68. package/src/loopEachPositionInRange.js +5 -0
  69. package/src/modulatePositionByRange.js +10 -0
  70. package/src/modulatePositionByRange.test.js +12 -0
  71. package/src/modulateRangeBySequenceLength.js +11 -0
  72. package/src/modulateRangeBySequenceLength.test.js +16 -0
  73. package/src/normalizePositionByRangeLength.js +20 -0
  74. package/src/normalizePositionByRangeLength.test.js +23 -0
  75. package/src/normalizePositionByRangeLength1Based.js +5 -0
  76. package/src/normalizePositionByRangeLength1Based.test.js +9 -0
  77. package/src/normalizeRange.js +11 -0
  78. package/src/normalizeRange.test.js +9 -0
  79. package/src/provideInclusiveOptions.js +26 -0
  80. package/src/reversePositionInRange.js +13 -0
  81. package/src/splitRangeIntoTwoPartsIfItIsCircular.js +31 -0
  82. package/src/splitRangeIntoTwoPartsIfItIsCircular.test.js +33 -0
  83. package/src/translateRange.js +9 -0
  84. package/src/translateRange.test.js +20 -0
  85. package/src/trimNonCicularRangeByAnotherNonCircularRange.js +57 -0
  86. package/src/trimNumberToFitWithin0ToAnotherNumber.js +12 -0
  87. package/src/trimRangeByAnotherRange.js +102 -0
  88. package/src/trimRangeByAnotherRange.test.js +314 -0
  89. package/src/zeroSubrangeByContainerRange.js +36 -0
  90. package/src/zeroSubrangeByContainerRange.test.js +51 -0
@@ -0,0 +1,49 @@
1
+
2
+
3
+ import getSequenceWithinRange from './getSequenceWithinRange.js';
4
+
5
+ import assert from 'assert';
6
+ let subseq;
7
+
8
+ describe('getSequenceWithinRange', function() {
9
+ it('works with an array (translation amino acids for example) as well', function() {
10
+ subseq = getSequenceWithinRange({start: 0,end:0},['a','t','g','c']);
11
+ assert.deepEqual(subseq, ['a']);
12
+ subseq = getSequenceWithinRange({start: 1,end:1},['a','t','g','c']);
13
+ assert.deepEqual(subseq, ['t']);
14
+ subseq = getSequenceWithinRange({start: 1,end:0},['a','t','g','c']);
15
+ assert.deepEqual(subseq, ['t','g','c','a']);
16
+ });
17
+ it('gets a non circular range', function() {
18
+ subseq = getSequenceWithinRange({start: 0,end:0},'atgc');
19
+ assert.equal(subseq, 'a');
20
+ subseq = getSequenceWithinRange({start: 1,end:1},'atgc');
21
+ assert.equal(subseq, 't');
22
+ subseq = getSequenceWithinRange({start: 0,end:3},'atgc');
23
+ assert.equal(subseq, 'atgc');
24
+ });
25
+ it('gets a circular range', function() {
26
+ subseq = getSequenceWithinRange({start: 1,end:0},'atgc');
27
+ assert.deepEqual(subseq, 'tgca');
28
+ subseq = getSequenceWithinRange({start: 2,end:1},'atgc');
29
+ assert.deepEqual(subseq, 'gcat');
30
+ subseq = getSequenceWithinRange({start: 3,end:0},'atgc');
31
+ assert.deepEqual(subseq, 'ca');
32
+ });
33
+ it('gets a circular range', function() {
34
+ subseq = getSequenceWithinRange({start: 1,end:0},'atgc');
35
+ assert.deepEqual(subseq, 'tgca');
36
+ subseq = getSequenceWithinRange({start: 2,end:1},'atgc');
37
+ assert.deepEqual(subseq, 'gcat');
38
+ subseq = getSequenceWithinRange({start: 3,end:0},'atgc');
39
+ assert.deepEqual(subseq, 'ca');
40
+ });
41
+ it('returns an empty string if the range is invalid', function() {
42
+ subseq = getSequenceWithinRange({start: -1,end:0},'atgc');
43
+ assert.deepEqual(subseq, '');
44
+ subseq = getSequenceWithinRange({start: -1,end:-1},'atgc');
45
+ assert.deepEqual(subseq, '');
46
+ subseq = getSequenceWithinRange({start: 3,end:-1},'atgc');
47
+ assert.deepEqual(subseq, '');
48
+ });
49
+ });
@@ -0,0 +1,12 @@
1
+ export default function getShortestDistanceBetweenTwoPositions(position1, position2, sequenceLength) {
2
+ if (position1 < position2) {
3
+ const position1Holder = position1;
4
+ position1 = position2
5
+ position2 = position1Holder
6
+ }
7
+ //position 1 is now always >= position 2
8
+
9
+ const d1 = position1 - position2;
10
+ const d2 = sequenceLength - position1 + position2;
11
+ return Math.min(d1,d2)
12
+ };
@@ -0,0 +1,14 @@
1
+ import assert from 'assert';
2
+ import getShortestDistanceBetweenTwoPositions from './getShortestDistanceBetweenTwoPositions';
3
+ describe('getShortestDistanceBetweenTwoPositions', function () {
4
+ it('should return the correct length for positions that cross the origin', function () {
5
+ const length = getShortestDistanceBetweenTwoPositions(9,0,10);
6
+ assert(length === 1)
7
+
8
+ });
9
+ it('should return the correct length for ranges that do not cross the origin', function () {
10
+ const length = getShortestDistanceBetweenTwoPositions(4,6,10);
11
+ assert(length === 2)
12
+
13
+ });
14
+ });
@@ -0,0 +1,24 @@
1
+ import checkIfPotentiallyCircularRangesOverlap from './checkIfPotentiallyCircularRangesOverlap';
2
+
3
+ export default function getYOffsetForPotentiallyCircularRange(range, YOffsetLevelsWithRanges, assignYOffsetToRange) {
4
+ //adjust the yOffset of the range being pushed in by checking its range against other range already in the row
5
+ let yOffset = [];
6
+ //YOffsetLevelsWithRanges is an array of arrays (array of yOffset levels holding arrays of range)
7
+ //loop through y offset levels starting with the 0 level until an empty one is found and push the range into it. If none are found, add another level.
8
+ const openYOffsetFound = YOffsetLevelsWithRanges.some(function(rangesAlreadyAddedToYOffset, index) {
9
+ const rangeBlocked = rangesAlreadyAddedToYOffset.some(function(comparisonRange) {
10
+ return checkIfPotentiallyCircularRangesOverlap(range, comparisonRange)
11
+ })
12
+ if (!rangeBlocked) {
13
+ yOffset = index
14
+ if (assignYOffsetToRange) range.yOffset = index
15
+ rangesAlreadyAddedToYOffset.push(range)
16
+ return true
17
+ }
18
+ });
19
+ if (!openYOffsetFound) {
20
+ yOffset = YOffsetLevelsWithRanges.length
21
+ if (assignYOffsetToRange) range.yOffset = YOffsetLevelsWithRanges.length
22
+ }
23
+ return yOffset
24
+ };
@@ -0,0 +1,20 @@
1
+ import getYOffsetForPotentiallyCircularRange from './getYOffsetForPotentiallyCircularRange';
2
+
3
+ export default function getYOffsetsForPotentiallyCircularRanges(ranges, assignYOffsetToRange) {
4
+ //adjust the yOffset of the range being pushed in by checking its range against other ranges already in the row
5
+ const yOffsets = [];
6
+ let maxYOffset = 0;
7
+ const yOffsetLevels = [] //yOffsetLevels is an array of arrays (array of yOffset levels holding arrays of ranges)
8
+ ranges.forEach(function(range){
9
+ //loop through y offset levels starting with the 0 level until an empty one is found and push the range into it. If none are found, add another level.
10
+ const yOffset = getYOffsetForPotentiallyCircularRange(range, yOffsetLevels, assignYOffsetToRange)
11
+ yOffsets.push(yOffset)
12
+ if (yOffset>maxYOffset) {
13
+ maxYOffset = yOffset;
14
+ }
15
+ range.yOffset = yOffset;
16
+ if (!yOffsetLevels[yOffset]) yOffsetLevels[yOffset] = [];
17
+ yOffsetLevels[yOffset].push(range);
18
+ });
19
+ return {yOffsets:yOffsets, maxYOffset: maxYOffset};
20
+ };
@@ -0,0 +1,29 @@
1
+ import {expect} from 'chai';
2
+ import getYOffsetsForPotentiallyCircularRanges from './getYOffsetsForPotentiallyCircularRanges.js';
3
+ describe('getYOffsetsForPotentiallyCircularRanges', function() {
4
+ it('returns correct yOffset for overlapping ranges', function() {
5
+ expect(getYOffsetsForPotentiallyCircularRanges([{
6
+ start: 5,
7
+ end: 100
8
+ }, {
9
+ start: 50,
10
+ end: 50
11
+ }])).to.deep.equal({yOffsets: [0,1], maxYOffset: 1});
12
+ expect(getYOffsetsForPotentiallyCircularRanges([{
13
+ start: 5,
14
+ end: 100
15
+ }, {
16
+ start: 50,
17
+ end: 50
18
+ },{
19
+ start: 50,
20
+ end: 50
21
+ },{
22
+ start: 150,
23
+ end: 4
24
+ },{
25
+ start: 150,
26
+ end: 150
27
+ }])).to.deep.equal({yOffsets: [0,1,2,0,1], maxYOffset: 2});
28
+ });
29
+ });
@@ -0,0 +1,17 @@
1
+
2
+ import getOverlapsOfPotentiallyCircularRanges from './getOverlapsOfPotentiallyCircularRanges';
3
+ import collapseOverlapsGeneratedFromRangeComparisonIfPossible from './collapseOverlapsGeneratedFromRangeComparisonIfPossible';
4
+ import zeroSubrangeByContainerRange from './zeroSubrangeByContainerRange';
5
+ import normalizePositionByRangeLength from './normalizePositionByRangeLength';
6
+
7
+ //gets the overlapping sections of two ranges and zeroes them to their first point of intersection!
8
+ export default function getZeroedRangeOverlaps (annotation, selection, sequenceLength) {
9
+ const overlaps = collapseOverlapsGeneratedFromRangeComparisonIfPossible(getOverlapsOfPotentiallyCircularRanges(annotation, selection, sequenceLength), sequenceLength, annotation)
10
+ const zeroedOverlaps = overlaps.map((overlap) => {
11
+ return zeroSubrangeByContainerRange(overlap, {
12
+ start: selection.start,
13
+ end: normalizePositionByRangeLength(selection.start - 1, sequenceLength)
14
+ }, sequenceLength)
15
+ })
16
+ return zeroedOverlaps
17
+ };
@@ -0,0 +1,36 @@
1
+ import assert from 'assert';
2
+ import getZeroedRangeOverlaps from './getZeroedRangeOverlaps';
3
+ describe('getZeroedRangeOverlaps', function() {
4
+ it('annotation non-circular, selection non circular ', function() {
5
+ const res = getZeroedRangeOverlaps({start: 0, end: 3}, {start: 2, end: 3}, 4, true, true)
6
+ assert.deepEqual(res, [{start: 0, end:1}])
7
+ })
8
+ it('annotation non circular, selection circular', function() {
9
+ //01234
10
+ //aaaa
11
+ //s sss
12
+ const res = getZeroedRangeOverlaps({start: 0, end: 3}, {start: 2, end: 0}, 5, true, true)
13
+ assert.deepEqual(res, [ {start: 3,end: 3}, {start: 0, end:1}])
14
+ })
15
+ it('annotation non circular, selection circular 2', function() {
16
+ //0123
17
+ //aaaa
18
+ //ssss
19
+ const res = getZeroedRangeOverlaps({start: 0, end: 3}, {start: 2, end: 1}, 4, true, true)
20
+ assert.deepEqual(res, [ {start: 2,end: 3}, {start: 0, end:1},])
21
+ })
22
+ it('annotation circular, selection fully containing', function() {
23
+ //01234
24
+ //aa aa
25
+ //sssss
26
+ const res = getZeroedRangeOverlaps({start: 3, end: 1}, {start: 0, end: 4}, 5, true, true)
27
+ assert.deepEqual(res, [{start: 3, end: 1}])
28
+ })
29
+ it('annotation circular, selection fully containing 2', function() {
30
+ //01234
31
+ //aa aa
32
+ //sssss
33
+ const res = getZeroedRangeOverlaps({start: 3, end: 1}, {start: 1, end: 0}, 5, true, true)
34
+ assert.deepEqual(res, [{start: 2, end: 4}, {start: 0, end: 0}])
35
+ })
36
+ })
package/src/index.js ADDED
@@ -0,0 +1,51 @@
1
+ export { default as adjustRangeToDeletionOfAnotherRange } from './adjustRangeToDeletionOfAnotherRange';
2
+ export { default as adjustRangeToInsert } from './adjustRangeToInsert';
3
+ export { default as checkIfNonCircularRangesOverlap } from './checkIfNonCircularRangesOverlap';
4
+ export { default as checkIfPotentiallyCircularRangesOverlap } from './checkIfPotentiallyCircularRangesOverlap';
5
+ export { default as collapseOverlapsGeneratedFromRangeComparisonIfPossible } from './collapseOverlapsGeneratedFromRangeComparisonIfPossible';
6
+ export { default as convertRangeIndices } from './convertRangeIndices';
7
+ export { default as convertRangeTo0Based } from './convertRangeTo0Based';
8
+ export { default as convertRangeTo1Based } from './convertRangeTo1Based';
9
+ export { default as doesRangeSpanEntireSequence } from './doesRangeSpanEntireSequence';
10
+ export { default as isRangeOrPositionWithinRange } from './isRangeOrPositionWithinRange';
11
+ export { default as doesRangeSpanOrigin } from './doesRangeSpanOrigin';
12
+ export { default as expandOrContractCircularRangeToPosition } from './expandOrContractCircularRangeToPosition';
13
+ export { default as expandOrContractNonCircularRangeToPosition } from './expandOrContractNonCircularRangeToPosition';
14
+ export { default as expandOrContractRangeByLength } from './expandOrContractRangeByLength';
15
+ export { default as expandOrContractRangeToPosition } from './expandOrContractRangeToPosition';
16
+ export { default as flipContainedRange } from './flipContainedRange';
17
+ export { default as generateRandomRange } from './generateRandomRange';
18
+ export { default as getAnnotationRangeType } from './getAnnotationRangeType';
19
+ export { default as getEachPositionInRangeAsArray } from './getEachPositionInRangeAsArray';
20
+ export { default as getLengthOfOverlappingRegionsBetweenTwoRanges } from './getLengthOfOverlappingRegionsBetweenTwoRanges';
21
+ export { default as getOverlapOfNonCircularRanges } from './getOverlapOfNonCircularRanges';
22
+ export { default as getOverlapsOfPotentiallyCircularRanges } from './getOverlapsOfPotentiallyCircularRanges';
23
+ export { default as getPositionFromAngle } from './getPositionFromAngle';
24
+ export { default as getRangeAngles } from './getRangeAngles';
25
+ export { default as getRangeLength } from './getRangeLength';
26
+ export { default as getMiddleOfRange } from './getMiddleOfRange';
27
+ export { default as getRangesBetweenTwoRanges } from './getRangesBetweenTwoRanges';
28
+ export { default as getSequenceWithinRange } from './getSequenceWithinRange';
29
+ export { default as getShortestDistanceBetweenTwoPositions } from './getShortestDistanceBetweenTwoPositions';
30
+ export { default as getYOffsetForPotentiallyCircularRange } from './getYOffsetForPotentiallyCircularRange';
31
+ export { default as getYOffsetsForPotentiallyCircularRanges } from './getYOffsetsForPotentiallyCircularRanges';
32
+ export { default as invertRange } from './invertRange';
33
+ export { default as isPositionCloserToRangeStartThanRangeEnd } from './isPositionCloserToRangeStartThanRangeEnd';
34
+ export { default as isPositionWithinRange } from './isPositionWithinRange';
35
+ export { default as isRangeWithinRange } from './isRangeWithinRange';
36
+ export { default as loopEachPositionInRange } from './loopEachPositionInRange';
37
+ export { default as modulatePositionByRange } from './modulatePositionByRange';
38
+ export { default as modulateRangeBySequenceLength } from './modulateRangeBySequenceLength';
39
+ export { default as normalizePositionByRangeLength } from './normalizePositionByRangeLength';
40
+ export { default as normalizePositionByRangeLength1Based } from './normalizePositionByRangeLength1Based';
41
+ export { default as normalizeRange } from './normalizeRange';
42
+ export { default as provideInclusiveOptions } from './provideInclusiveOptions';
43
+ export { default as reversePositionInRange } from './reversePositionInRange';
44
+ export { default as splitRangeIntoTwoPartsIfItIsCircular } from './splitRangeIntoTwoPartsIfItIsCircular';
45
+ export { default as translateRange } from './translateRange';
46
+ export { default as trimNonCicularRangeByAnotherNonCircularRange } from './trimNonCicularRangeByAnotherNonCircularRange';
47
+ export { default as trimNumberToFitWithin0ToAnotherNumber } from './trimNumberToFitWithin0ToAnotherNumber';
48
+ export { default as trimRangeByAnotherRange } from './trimRangeByAnotherRange';
49
+ export { default as zeroSubrangeByContainerRange } from './zeroSubrangeByContainerRange';
50
+ export { default as adjustRangeToRotation } from './adjustRangeToRotation';
51
+ export { default as getZeroedRangeOverlaps } from './getZeroedRangeOverlaps';
@@ -0,0 +1,33 @@
1
+ import * as src from "./index";
2
+ import fs from "fs";
3
+
4
+ describe("index.js", () => {
5
+ it(`should export all functions defined`, () => {
6
+ return new Promise((resolve) => {
7
+
8
+ fs.readdir(__dirname, (err, files) => {
9
+ let passes = true;
10
+ files.forEach(file => {
11
+ if (
12
+ file.indexOf(".test.js") > -1 ||
13
+ file.indexOf("index.js") > -1
14
+ ) {
15
+ return;
16
+ }
17
+ const funcOrObj = src[file.replace(".js", "")];
18
+ if (!funcOrObj) {
19
+ console.info(
20
+ `Uh oh, it looks like you forgot to export (or explicitly ignore) this file:`,
21
+ file
22
+ );
23
+ passes = false;
24
+ }
25
+ });
26
+ if (!passes) {
27
+ throw new Error("Please make sure to export (or ignore) each file! Update index.js to export the file");
28
+ }
29
+ resolve();
30
+ });
31
+ })
32
+ });
33
+ });
@@ -0,0 +1,21 @@
1
+ import normalizePositionByRangeLength from "./normalizePositionByRangeLength";
2
+ import provideInclusiveOptions from "./provideInclusiveOptions";
3
+ export default provideInclusiveOptions(invertRange);
4
+
5
+ function invertRange(rangeOrCaret, rangeMax) {
6
+ if (rangeOrCaret.start > -1) {
7
+ const start = rangeOrCaret.end + 1;
8
+ const end = rangeOrCaret.start - 1;
9
+ return {
10
+ start: normalizePositionByRangeLength(start, rangeMax, false),
11
+ end: normalizePositionByRangeLength(end, rangeMax, false)
12
+ };
13
+ } else {
14
+ if (rangeOrCaret > -1) {
15
+ return {
16
+ start: normalizePositionByRangeLength(rangeOrCaret, rangeMax, false),
17
+ end: normalizePositionByRangeLength(rangeOrCaret - 1, rangeMax, false)
18
+ };
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,96 @@
1
+
2
+ import invertRange from './invertRange';
3
+ import chai from 'chai';
4
+ chai.should();
5
+ describe('invertRange', function () {
6
+ it('should invert a non-circular range', function () {
7
+ const invertedRange = invertRange({start: 2, end:2}, 10);
8
+ invertedRange.start.should.equal(3)
9
+ invertedRange.end.should.equal(1)
10
+ });
11
+ it('should invert a non-circular range', function () {
12
+ const invertedRange = invertRange({start: 0, end:2}, 10);
13
+ invertedRange.start.should.equal(3)
14
+ invertedRange.end.should.equal(9)
15
+ });
16
+ it('should invert a non-circular range', function () {
17
+ const invertedRange = invertRange({start: 0, end:9}, 10);
18
+ invertedRange.start.should.equal(0)
19
+ invertedRange.end.should.equal(9)
20
+ });
21
+ it('should invert a non-circular range', function () {
22
+ const invertedRange = invertRange({start: 4, end:9}, 10);
23
+ invertedRange.start.should.equal(0)
24
+ invertedRange.end.should.equal(3)
25
+ });
26
+ it('should invert a circular range', function () {
27
+ const invertedRange = invertRange({start: 3, end:1}, 10);
28
+ invertedRange.start.should.equal(2)
29
+ invertedRange.end.should.equal(2)
30
+ });
31
+ it('should invert a circular range', function () {
32
+ const invertedRange = invertRange({start: 9, end:1}, 10);
33
+ invertedRange.start.should.equal(2)
34
+ invertedRange.end.should.equal(8)
35
+ });
36
+ it('should invert a circular range', function () {
37
+ const invertedRange = invertRange({start: 3, end:0}, 10);
38
+ invertedRange.start.should.equal(1)
39
+ invertedRange.end.should.equal(2)
40
+ });
41
+ it('should handle inverting a whole range by returning the original range', function () {
42
+ const invertedRange = invertRange({start: 4, end:3}, 10);
43
+ invertedRange.start.should.equal(4)
44
+ invertedRange.end.should.equal(3)
45
+ });
46
+ it('should handle inverting a caret position', function () {
47
+ const invertedRange = invertRange(1, 10);
48
+ invertedRange.start.should.equal(1)
49
+ invertedRange.end.should.equal(0)
50
+ });
51
+ it('should handle inverting a caret position', function () {
52
+ const invertedRange = invertRange(0, 10);
53
+ invertedRange.start.should.equal(0)
54
+ invertedRange.end.should.equal(9)
55
+ });
56
+ //tnrtodo: maybe one day we'll want to handle the "entire range" case in a special way, but for now we'll just return the original range
57
+ // it('should handle inverting a whole range by setting the start and end to -1', function () {
58
+ // var invertedRange = invertRange({start: 4, end:3}, 10);
59
+ // invertedRange.start.should.equal(-1)
60
+ // invertedRange.end.should.equal(-1)
61
+ // });
62
+ });
63
+ describe('invertRange should handle options inclusive1BasedEnd or inclusive1BasedStart', function () {
64
+ it('should handle inverting a whole range by returning the original range', function () {
65
+ const options = {inclusive1BasedEnd: true};
66
+ const invertedRange = invertRange({start: 2, end:2}, 10, options);
67
+ invertedRange.start.should.equal(2)
68
+ invertedRange.end.should.equal(2)
69
+ });
70
+ it('should invert a non-circular range', function () {
71
+ const options = {inclusive1BasedEnd: true};
72
+ const invertedRange = invertRange({start: 0, end:2}, 10, options);
73
+ invertedRange.start.should.equal(2)
74
+ invertedRange.end.should.equal(10)
75
+ });
76
+ it('should invert non-circular range 1', function () {
77
+ const invertedRange = invertRange({start: 0, end:9}, 10,{inclusive1BasedEnd: true});
78
+ invertedRange.start.should.equal(9)
79
+ invertedRange.end.should.equal(10)
80
+ });
81
+ it('should invert a non-circular range 2', function () {
82
+ const invertedRange = invertRange({start: 1, end:9}, 10,{inclusive1BasedEnd: true,inclusive1BasedStart: true});
83
+ invertedRange.start.should.equal(10)
84
+ invertedRange.end.should.equal(10)
85
+ });
86
+ it('should invert a non-circular range 3', function () {
87
+ const invertedRange = invertRange({start: 3, end:6}, 10,{inclusive1BasedEnd: true,inclusive1BasedStart: true});
88
+ invertedRange.start.should.equal(7)
89
+ invertedRange.end.should.equal(2)
90
+ });
91
+ it('should invert a circular range 4', function () {
92
+ const invertedRange = invertRange({start: 6, end:3}, 10,{inclusive1BasedEnd: true,inclusive1BasedStart: true});
93
+ invertedRange.start.should.equal(4)
94
+ invertedRange.end.should.equal(5)
95
+ });
96
+ });
@@ -0,0 +1,8 @@
1
+ //function to calculate whether a position is closer to the range start than the range end
2
+ import getShortestDistanceBetweenTwoPositions from './getShortestDistanceBetweenTwoPositions';
3
+
4
+ export default function isPositionCloserToRangeStartThanRangeEnd(position, range, maxLength) {
5
+ const distanceFromStart = getShortestDistanceBetweenTwoPositions(range.start, position, maxLength);
6
+ const distanceFromEnd = getShortestDistanceBetweenTwoPositions(range.end, position, maxLength);
7
+ return distanceFromStart <= distanceFromEnd
8
+ };
@@ -0,0 +1,17 @@
1
+ import isPositionCloserToRangeStartThanRangeEnd from './isPositionCloserToRangeStartThanRangeEnd';
2
+ import {expect} from 'chai';
3
+
4
+ describe('isPositionCloserToRangeStartThanRangeEnd', function() {
5
+ it('should correctly determine whether a position is closer to the start of a range than the end', function() {
6
+ expect(isPositionCloserToRangeStartThanRangeEnd(0,{start: 1, end: 10}, 100)).to.equal(true)
7
+ expect(isPositionCloserToRangeStartThanRangeEnd(1,{start: 0, end: 10}, 100)).to.equal(true)
8
+ expect(isPositionCloserToRangeStartThanRangeEnd(11,{start: 1, end: 10}, 100)).to.equal(false)
9
+ expect(isPositionCloserToRangeStartThanRangeEnd(0,{start: 0, end: 10}, 100)).to.equal(true)
10
+ expect(isPositionCloserToRangeStartThanRangeEnd(10,{start: 0, end: 10}, 100)).to.equal(false)
11
+ expect(isPositionCloserToRangeStartThanRangeEnd(10,{start: 10, end: 5}, 100)).to.equal(true)
12
+ expect(isPositionCloserToRangeStartThanRangeEnd(11,{start: 10, end: 5}, 100)).to.equal(true)
13
+ expect(isPositionCloserToRangeStartThanRangeEnd(4,{start: 10, end: 5}, 100)).to.equal(false)
14
+ expect(isPositionCloserToRangeStartThanRangeEnd(5,{start: 10, end: 5}, 100)).to.equal(false)
15
+ expect(isPositionCloserToRangeStartThanRangeEnd(6,{start: 10, end: 5}, 100)).to.equal(false)
16
+ })
17
+ })
@@ -0,0 +1,31 @@
1
+ import splitRangeIntoTwoPartsIfItIsCircular from "./splitRangeIntoTwoPartsIfItIsCircular";
2
+ /**
3
+ *
4
+ * @param {*} position //assumed to be a 0 based "caretPosition"
5
+ * @param {*} range //0 based inclusive range
6
+ * @param {*} sequenceLength
7
+ * @param {*} includeEdges - (default false) whether or not to say
8
+ */
9
+ function isPositionWithinRange(
10
+ position,
11
+ range,
12
+ sequenceLength,
13
+ includeStartEdge,
14
+ includeEndEdge
15
+ ) {
16
+ const ranges = splitRangeIntoTwoPartsIfItIsCircular(range, sequenceLength);
17
+ const positionFits = ranges.some(function(range) {
18
+ if (includeStartEdge ? position < range.start : position <= range.start) {
19
+ return false;
20
+ } else {
21
+ if (includeEndEdge ? position <= range.end + 1 : position <= range.end) {
22
+ return true;
23
+ } else {
24
+ return false;
25
+ }
26
+ }
27
+ });
28
+ return positionFits;
29
+ }
30
+
31
+ export default isPositionWithinRange;
@@ -0,0 +1,29 @@
1
+ import {isObject} from "lodash";
2
+ import isRangeWithinRange from "./isRangeWithinRange";
3
+ import isPositionWithinRange from "./isPositionWithinRange";
4
+
5
+ export default function isRangeOrPositionWithinRange(
6
+ rangeOrPositionToCheck,
7
+ containingRange,
8
+ maxLength,
9
+ includeStartEdge,
10
+ includeEndEdge
11
+ ) {
12
+
13
+ if (rangeOrPositionToCheck === undefined || rangeOrPositionToCheck === null ||
14
+ containingRange === undefined || containingRange === null ) {
15
+ return false
16
+ }
17
+ if (isObject(rangeOrPositionToCheck)) {
18
+ return isRangeWithinRange(rangeOrPositionToCheck,
19
+ containingRange,
20
+ maxLength)
21
+ } else {
22
+ return isPositionWithinRange( rangeOrPositionToCheck,
23
+ containingRange,
24
+ maxLength,
25
+ includeStartEdge,
26
+ includeEndEdge)
27
+ }
28
+
29
+ };
@@ -0,0 +1,150 @@
1
+ import isRangeOrPositionWithinRange from "./isRangeOrPositionWithinRange";
2
+ import {expect} from "chai";
3
+
4
+ describe("isRangeOrPositionWithinRange", function() {
5
+ it("should correctly determine whether a position is within a range", function() {
6
+ expect(isRangeOrPositionWithinRange(1, { start: 1, end: 1 })).to.equal(
7
+ false
8
+ );
9
+ expect(isRangeOrPositionWithinRange(0, { start: 1, end: 10 })).to.equal(
10
+ false
11
+ );
12
+ expect(isRangeOrPositionWithinRange(1, { start: 0, end: 10 })).to.equal(
13
+ true
14
+ );
15
+ expect(isRangeOrPositionWithinRange(11, { start: 1, end: 10 })).to.equal(
16
+ false
17
+ );
18
+ expect(isRangeOrPositionWithinRange(0, { start: 0, end: 10 })).to.equal(
19
+ false
20
+ );
21
+ expect(isRangeOrPositionWithinRange(10, { start: 0, end: 10 })).to.equal(
22
+ true
23
+ );
24
+ expect(isRangeOrPositionWithinRange(10, { start: 10, end: 5 })).to.equal(
25
+ false
26
+ );
27
+ expect(isRangeOrPositionWithinRange(11, { start: 10, end: 5 })).to.equal(
28
+ true
29
+ );
30
+ expect(isRangeOrPositionWithinRange(4, { start: 10, end: 5 })).to.equal(
31
+ true
32
+ );
33
+ expect(isRangeOrPositionWithinRange(5, { start: 10, end: 5 })).to.equal(
34
+ true
35
+ );
36
+ expect(isRangeOrPositionWithinRange(6, { start: 10, end: 5 })).to.equal(
37
+ false
38
+ );
39
+ });
40
+
41
+ it("should correctly determine whether a position is within a range when includeStartEdge/includeEndEdge is set to true", function() {
42
+ expect(
43
+ isRangeOrPositionWithinRange(1, { start: 1, end: 1 }, 11, true, true)
44
+ ).to.equal(true);
45
+ expect(
46
+ isRangeOrPositionWithinRange(0, { start: 0, end: 10 }, 11, true, true)
47
+ ).to.equal(true);
48
+ });
49
+
50
+ it("should work for angle values w/ long decimal places", function() {
51
+ expect(
52
+ isRangeOrPositionWithinRange(
53
+ { start: 5.669848916850995, end: 5.815135586893387 },
54
+ { start: 4.71238898038469, end: 1.5707963267948966 },
55
+ Math.PI * 2 + 1 //need the +1
56
+ )
57
+ ).to.equal(true);
58
+ });
59
+ it("should correctly determine whether a position is within a range", function() {
60
+ expect(
61
+ isRangeOrPositionWithinRange(null, { start: 1, end: 10 }, 100)
62
+ ).to.equal(false);
63
+ expect(
64
+ isRangeOrPositionWithinRange({}, { start: 1, end: 10 }, 100)
65
+ ).to.equal(false);
66
+ expect(
67
+ isRangeOrPositionWithinRange({ start: 5, end: 10 }, undefined, 100)
68
+ ).to.equal(false);
69
+ expect(isRangeOrPositionWithinRange(undefined, undefined, 100)).to.equal(
70
+ false
71
+ );
72
+ expect(
73
+ isRangeOrPositionWithinRange(
74
+ { start: 5, end: 10 },
75
+ { start: 1, end: 10 },
76
+ 100
77
+ )
78
+ ).to.equal(true);
79
+ expect(
80
+ isRangeOrPositionWithinRange(
81
+ { start: 5, end: 10 },
82
+ { start: -1, end: 10 },
83
+ 100
84
+ )
85
+ ).to.equal(false);
86
+ expect(
87
+ isRangeOrPositionWithinRange(
88
+ { start: 0, end: 10 },
89
+ { start: 0, end: 10 },
90
+ 100
91
+ )
92
+ ).to.equal(true);
93
+ expect(
94
+ isRangeOrPositionWithinRange(
95
+ { start: 10, end: 10 },
96
+ { start: 1, end: 10 },
97
+ 100
98
+ )
99
+ ).to.equal(true);
100
+ expect(
101
+ isRangeOrPositionWithinRange(
102
+ { start: 11, end: 10 },
103
+ { start: 0, end: 10 },
104
+ 100
105
+ )
106
+ ).to.equal(false);
107
+ expect(
108
+ isRangeOrPositionWithinRange(
109
+ { start: 12, end: 16 },
110
+ { start: 0, end: 10 },
111
+ 100
112
+ )
113
+ ).to.equal(false);
114
+ expect(
115
+ isRangeOrPositionWithinRange(
116
+ { start: 10, end: 5 },
117
+ { start: 10, end: 5 },
118
+ 100
119
+ )
120
+ ).to.equal(true);
121
+ expect(
122
+ isRangeOrPositionWithinRange(
123
+ { start: 10, end: 5 },
124
+ { start: 10, end: 5 },
125
+ 100
126
+ )
127
+ ).to.equal(true);
128
+ expect(
129
+ isRangeOrPositionWithinRange(
130
+ { start: 10, end: 5 },
131
+ { start: 10, end: 5 },
132
+ 100
133
+ )
134
+ ).to.equal(true);
135
+ expect(
136
+ isRangeOrPositionWithinRange(
137
+ { start: 0, end: 6 },
138
+ { start: 10, end: 5 },
139
+ 100
140
+ )
141
+ ).to.equal(false);
142
+ expect(
143
+ isRangeOrPositionWithinRange(
144
+ { start: 10, end: 6 },
145
+ { start: 10, end: 5 },
146
+ 100
147
+ )
148
+ ).to.equal(false);
149
+ });
150
+ });