@fw-components/formula-editor 2.0.7-formula-editor-enhancements.12 → 2.0.7-formula-editor.21

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.
@@ -1,199 +1,152 @@
1
1
  import Big from "big.js";
2
- import { Expectation, Queue, Stack } from "./helpers.js";
3
2
  import { Recommender } from "./recommendor.js";
3
+ import { Stack } from "./stack.js";
4
+ import { Queue } from "./queue.js";
5
+ import { Expectation } from "../types";
6
+ import { mathematicalOperators, operatorPrecedence } from "./constants.js";
4
7
  export class Parser {
5
8
  constructor(variables, minSuggestionLen) {
6
- this.mathematicalOperators = new Set(["^", "+", "-", "*", "/"]);
7
- this.operatorPrecedence = {
8
- "^": 3,
9
- "/": 2,
10
- "*": 2,
11
- "+": 1,
12
- "-": 1,
13
- };
14
9
  this.variables = variables;
15
- this._recommender = new Recommender(this.variables, minSuggestionLen);
10
+ this._recommender = new Recommender(Array.from(this.variables.keys()), minSuggestionLen);
16
11
  }
17
12
  parseInput(formula, prevCurPos = null, recommendation = null) {
18
- let tokens = formula.match(/'[^']*'|\d+|[A-Za-z_][A-Za-z0-9_]*|[-+(),*^/:?\s]/g);
19
- // Stores the positions of opening parentheses. This allows us to
20
- // show "Unclosed parenthesis error" for positions which are far behind
21
- // our current token
22
- let parentheses = new Stack();
23
- // The HTML formatted string which we eventually show on the view.
24
- let formattedString = ``;
25
- // The expectation that we have for the current token.
13
+ const tokens = formula.match(/'[^']*'|\d+|[A-Za-z_][A-Za-z0-9_]*|[-+(),*^/:?\s]/g);
14
+ const parentheses = new Stack();
26
15
  let expectation = Expectation.VARIABLE;
27
- // Position of the current token in the formula string.
28
16
  let currentPosition = 0;
29
- // Previous 'token' (not a space or a new line) that we just encountered.
30
17
  let previousToken = "";
31
- let currentTokens = "";
32
- // The object that we return as the output of the parsing result.
33
- let parseOutput = {
34
- recommendations: null,
35
- formattedContent: null,
36
- formattedString: null,
18
+ let parsedString = "";
19
+ const parseOutput = {
20
+ recommendations: [],
21
+ formattedString: "",
37
22
  newCursorPosition: prevCurPos ?? -1,
38
23
  errorString: null,
39
24
  };
40
- if (!formula.trim()) {
41
- if (recommendation) {
42
- formattedString = `${recommendation}`;
43
- currentPosition += recommendation.length;
44
- const parser = new DOMParser();
45
- const doc = parser.parseFromString(formattedString, "text/html");
46
- parseOutput.formattedContent = doc.querySelector("body");
47
- parseOutput.formattedString = formattedString;
48
- parseOutput.newCursorPosition = recommendation.length;
49
- return parseOutput;
50
- }
25
+ if (!formula.trim() && recommendation) {
26
+ parseOutput.formattedString = recommendation;
27
+ parseOutput.newCursorPosition = recommendation.length;
28
+ return parseOutput;
51
29
  }
52
30
  tokens?.forEach((token) => {
53
- // It is a number is either it's in the defined variables, or
54
- // it's a valid number literal.
55
31
  let isNumber = token.trim() !== "" && (this.variables.has(token) || !Number.isNaN(Number(token)));
56
- let isOperator = this.mathematicalOperators.has(token);
57
- let isSpace = token.trim() == "";
58
- let isBracket = token == "(" || token == ")";
32
+ const isOperator = mathematicalOperators.has(token);
33
+ const isSpace = token.trim() === "";
34
+ const isBracket = token === "(" || token === ")";
59
35
  if (isSpace) {
60
- formattedString = `${formattedString}${token}`;
36
+ parseOutput.formattedString += token;
61
37
  currentPosition += token.length;
62
38
  return;
63
39
  }
64
- // If the cursor position is 'inside` the current token:
65
- //
66
- // 1. If we've got a recommendation to add, simply replace the
67
- // word with the recommendation.
68
- // 2. Ask the recommendor to fetch recommendations for this specific
69
- // token/word.
40
+ /**
41
+ * Check if the cursor is in between the formula string
42
+ *
43
+ * - If we've got a recommendation to add, replace the word with the recommendation
44
+ * - Update recommendations based on the token/word
45
+ */
70
46
  if (currentPosition <= prevCurPos && currentPosition + token.length >= prevCurPos) {
71
47
  if (recommendation) {
72
- // Since we are sure that the recommendation will always correspond
73
- // to a variable.
74
48
  isNumber = true;
75
- if (this.mathematicalOperators.has(token)) {
76
- // append recommendation at the end if token is an operator
49
+ if (mathematicalOperators.has(token)) {
77
50
  const updatedTokenString = `${token} ${recommendation}`;
78
- formattedString += updatedTokenString;
51
+ parseOutput.formattedString += updatedTokenString;
79
52
  currentPosition += updatedTokenString.length;
80
53
  parseOutput.newCursorPosition = currentPosition;
81
54
  recommendation = null;
82
55
  return;
83
56
  }
84
57
  ;
85
- // If the new cursor length somehow becomes larger than the
86
- // length of the formula string, setting the caret to that
87
- // length will move the caret to the start. Although this overflow
88
- // won't happen, but still, this check prevents that.
89
58
  const updatedTokenLength = recommendation.length - token.length;
90
59
  parseOutput.newCursorPosition = Math.min(parseOutput.newCursorPosition, formula.length) + updatedTokenLength;
91
60
  token = recommendation;
92
61
  recommendation = null;
93
62
  }
94
- parseOutput.recommendations = this._recommender.getRecommendation(token);
63
+ parseOutput.recommendations = this._recommender.getRecommendations(token);
95
64
  }
96
- let tokenClassName = "";
97
- // Don't check for errors if an error has already been encountered.
65
+ /**
66
+ * Error checks
67
+ * skip error check if there is one already
68
+ */
98
69
  if (expectation != Expectation.UNDEFINED) {
99
- if (this.mathematicalOperators.has(previousToken) && isOperator) {
70
+ if (mathematicalOperators.has(previousToken) && isOperator) {
100
71
  parseOutput.errorString = `Multiple operators at position ${currentPosition}`;
101
72
  expectation = Expectation.UNDEFINED;
102
73
  }
103
- // Unnecessary closing parenthesis
104
- else if (parentheses.isEmpty() && token == ")") {
74
+ else if (parentheses.isEmpty() && token === ")") {
105
75
  parseOutput.errorString = `Unexpected ')' at position ${currentPosition}`;
106
- tokenClassName += " error";
107
76
  expectation = Expectation.UNDEFINED;
108
77
  }
109
- // Operator or ) after an operator. Eg: `23 / *` or `23 / )`
110
- // Unary `+` and `-` are not an error as they might represent
111
- // a positive or negative number respectively. But they will still
112
- // be an error if the formula ends with them.
113
- else if (expectation == Expectation.VARIABLE &&
114
- !isNumber &&
115
- !isSpace &&
116
- token != "(" &&
117
- !((token == "-" || token == "+") &&
118
- (!currentTokens.trim() || previousToken === "(" || this.mathematicalOperators.has(previousToken)))) {
78
+ /**
79
+ * Operator or ')' after an operator (Eg: '23 / *' or '23 / )')
80
+ * No error for Unary `+` and `-` as they might represent a positive or negative number respectively
81
+ */
82
+ else if (expectation === Expectation.VARIABLE && !isNumber && !isSpace && token != "("
83
+ && !((token === "-" || token === "+") && (!parsedString.trim() || previousToken === "(" || mathematicalOperators.has(previousToken)))) {
119
84
  parseOutput.errorString = `Expected variable/number at position ${currentPosition}`;
120
- tokenClassName += " error";
121
85
  expectation = Expectation.UNDEFINED;
122
86
  }
123
- // Number/Variable after the same. Eg: `a a` or `420 420`.
124
- // Having a ) is fine. Eg: `23)` might be representing `(23 + 23)
125
- else if (expectation == Expectation.OPERATOR &&
126
- !isOperator &&
127
- !isSpace &&
128
- token != ")") {
87
+ /**
88
+ * Multiple number/variable together without operator
89
+ */
90
+ else if (expectation === Expectation.OPERATOR && !isOperator && !isSpace && token != ")") {
129
91
  parseOutput.errorString = `Expected mathematical operator at position ${currentPosition}`;
130
- tokenClassName += " error";
131
92
  expectation = Expectation.UNDEFINED;
132
93
  }
133
- // Unknown symbol/variable/word
94
+ /**
95
+ * Unknown symbol/variable/word
96
+ */
134
97
  else if (!(isNumber || isOperator || isBracket || isSpace)) {
135
98
  parseOutput.errorString = `Unknown word at position ${currentPosition}`;
136
- tokenClassName += " error";
137
99
  expectation = Expectation.UNDEFINED;
138
100
  }
139
- // The case of division by zero. Since we can't know if an expression evaluates
140
- // to zero or not, that case can only be handled during calculation.
141
- else if (isNumber &&
142
- previousToken == "/" &&
143
- (this.variables.get(token) == 0 || Number(token) == 0)) {
101
+ /**
102
+ * division by zero
103
+ */
104
+ else if (isNumber && previousToken === "/" && (this.variables.get(token) === 0 || Number(token) === 0)) {
144
105
  parseOutput.errorString = `Division by zero at position ${currentPosition}`;
145
- tokenClassName += " error";
146
106
  expectation = Expectation.UNDEFINED;
147
107
  }
148
- // Empty brackets. Default might be takn as 0, but that will only make sense
149
- // in addition and subtraction and not in other operators, so making this
150
- // case an error makes more sense.
151
- else if (previousToken == "(" && token == ")") {
108
+ /**
109
+ * Empty brackets
110
+ */
111
+ else if (previousToken === "(" && token === ")") {
152
112
  parseOutput.errorString = `Empty brackets at position ${currentPosition}`;
153
- tokenClassName += " error";
154
113
  expectation = Expectation.UNDEFINED;
155
114
  }
156
115
  }
157
- // Setting the expectation for the next token, if we have not encountered an
158
- // error already.
116
+ /**
117
+ * Setting the expectation for the next token, if no error is there till now
118
+ */
159
119
  if (expectation != Expectation.UNDEFINED) {
160
- if (token == "(" || isOperator) {
120
+ if (token === "(" || isOperator) {
161
121
  expectation = Expectation.VARIABLE;
162
122
  }
163
- else if (token == ")" || isNumber) {
123
+ else if (token === ")" || isNumber) {
164
124
  expectation = Expectation.OPERATOR;
165
125
  }
166
126
  }
167
- if (token == "(")
127
+ if (token === "(")
168
128
  parentheses.push(currentPosition);
169
- else if (token == ")")
129
+ else if (token === ")")
170
130
  parentheses.pop();
171
- formattedString = `${formattedString}${token}`;
172
- previousToken = token;
131
+ parseOutput.formattedString += token;
173
132
  currentPosition += token.length;
174
- currentTokens += token;
133
+ parsedString += token;
134
+ previousToken = token;
175
135
  });
176
136
  if (recommendation) {
177
- parseOutput.newCursorPosition = Math.min(parseOutput.newCursorPosition +
178
- recommendation.length, formula.length + recommendation.length);
179
- formattedString = `${formattedString}${recommendation}`;
137
+ parseOutput.newCursorPosition = Math.min(parseOutput.newCursorPosition, formula.length) + recommendation.length;
138
+ parseOutput.formattedString += recommendation;
139
+ previousToken = recommendation;
180
140
  }
181
- // formula ends with a mathematical operator
182
- if (this.mathematicalOperators.has(previousToken) || !previousToken.trim().length) {
141
+ if (mathematicalOperators.has(previousToken) || !previousToken.trim().length) {
183
142
  parseOutput.recommendations = !parseOutput.errorString?.length ? Array.from(this.variables.keys()) : [];
184
143
  }
185
- //formula ends with mathematical operator
186
- if (this.mathematicalOperators.has(previousToken)) {
144
+ if (mathematicalOperators.has(previousToken)) {
187
145
  parseOutput.errorString = `Unexpected ending with mathematical operator at position: ${currentPosition}`;
188
146
  }
189
- // formula has unclosed `(`
190
147
  if (!parentheses.isEmpty()) {
191
148
  parseOutput.errorString = `Unclosed '(' at position: ${parentheses.top()}`;
192
149
  }
193
- const parser = new DOMParser();
194
- const doc = parser.parseFromString(formattedString, "text/html");
195
- parseOutput.formattedContent = doc.querySelector("body");
196
- parseOutput.formattedString = formattedString;
197
150
  return parseOutput;
198
151
  }
199
152
  buildRPN(formula) {
@@ -209,8 +162,8 @@ export class Parser {
209
162
  const parsedTokens = [];
210
163
  let currentTokens = "";
211
164
  for (const token of tokens) {
212
- if ((token == "+" || token == "-") &&
213
- (!currentTokens.trim() || previousToken === "(" || this.mathematicalOperators.has(previousToken))) {
165
+ if ((token === "+" || token === "-") &&
166
+ (!currentTokens.trim() || previousToken === "(" || mathematicalOperators.has(previousToken))) {
214
167
  carriedToken = token;
215
168
  }
216
169
  else if (carriedToken) {
@@ -229,19 +182,19 @@ export class Parser {
229
182
  const operatorStack = new Stack();
230
183
  const outputQueue = new Queue();
231
184
  for (const token of parsedTokens) {
232
- if (token == "(") {
185
+ if (token === "(") {
233
186
  operatorStack.push("(");
234
187
  }
235
- else if (token == ")") {
188
+ else if (token === ")") {
236
189
  while (operatorStack.top() != "(") {
237
190
  outputQueue.enqueue(operatorStack.pop());
238
191
  }
239
192
  operatorStack.pop();
240
193
  }
241
- else if (this.mathematicalOperators.has(token)) {
242
- while (this.mathematicalOperators.has(operatorStack.top()) &&
243
- this.operatorPrecedence[token] <=
244
- this.operatorPrecedence[operatorStack.top()]) {
194
+ else if (mathematicalOperators.has(token)) {
195
+ while (mathematicalOperators.has(operatorStack.top()) &&
196
+ operatorPrecedence[token] <=
197
+ operatorPrecedence[operatorStack.top()]) {
245
198
  outputQueue.enqueue(operatorStack.pop());
246
199
  }
247
200
  operatorStack.push(token);
@@ -285,7 +238,7 @@ export class Parser {
285
238
  // them with the current one, adds brackets accordingly to the `results`
286
239
  // around it, and then finally add it to the `operatorStack` for
287
240
  // future reference.
288
- else if (Object.keys(this.operatorPrecedence).includes(symbol)) {
241
+ else if (Object.keys(operatorPrecedence).includes(symbol)) {
289
242
  let [rightExpression, leftExpression, operatorA, operatorB] = [
290
243
  resultStack.pop(),
291
244
  resultStack.pop(),
@@ -293,20 +246,20 @@ export class Parser {
293
246
  operatorStack.pop(),
294
247
  ];
295
248
  // The conditions that govern when to show a parenthesis.
296
- if (this.operatorPrecedence[operatorB] <=
297
- this.operatorPrecedence[symbol] ||
298
- (this.operatorPrecedence[operatorB] ===
299
- this.operatorPrecedence[symbol] &&
249
+ if (operatorPrecedence[operatorB] <=
250
+ operatorPrecedence[symbol] ||
251
+ (operatorPrecedence[operatorB] ===
252
+ operatorPrecedence[symbol] &&
300
253
  ["/", "-"].includes(symbol))) {
301
254
  parsedLeftExpression = `(${leftExpression})`;
302
255
  }
303
256
  else {
304
257
  parsedLeftExpression = leftExpression;
305
258
  }
306
- if (this.operatorPrecedence[operatorA] <=
307
- this.operatorPrecedence[symbol] ||
308
- (this.operatorPrecedence[operatorA] ===
309
- this.operatorPrecedence[symbol] &&
259
+ if (operatorPrecedence[operatorA] <=
260
+ operatorPrecedence[symbol] ||
261
+ (operatorPrecedence[operatorA] ===
262
+ operatorPrecedence[symbol] &&
310
263
  ["/", "-"].includes(symbol))) {
311
264
  parsedRightExpression = `(${rightExpression})`;
312
265
  }
@@ -338,7 +291,7 @@ export class Parser {
338
291
  let calcStack = new Stack();
339
292
  while (!rpn.isEmpty()) {
340
293
  const frontItem = rpn.dequeue();
341
- if (!this.mathematicalOperators.has(frontItem)) {
294
+ if (!mathematicalOperators.has(frontItem)) {
342
295
  const [sign, variableKey] = /^[+-]/.test(frontItem) ? [frontItem[0], frontItem.slice(1)] : ["", frontItem];
343
296
  const operandValue = Number.parseFloat(this.variables.get(variableKey)?.toString() ?? variableKey);
344
297
  const number = Number.parseFloat(sign + "1") * operandValue;
@@ -360,7 +313,7 @@ export class Parser {
360
313
  calcStack.push(Big(numA).mul(Big(numB)));
361
314
  break;
362
315
  case "/":
363
- if (parseFloat(Big(numB).toString()) == 0) {
316
+ if (parseFloat(Big(numB).toString()) === 0) {
364
317
  calculationResult.errorString = "Division by zero encountered";
365
318
  return calculationResult;
366
319
  }
@@ -0,0 +1,28 @@
1
+ export class Queue {
2
+ constructor() {
3
+ this._elements = {};
4
+ this._head = 0;
5
+ this._tail = 0;
6
+ }
7
+ enqueue(item) {
8
+ this._elements[this._tail] = item;
9
+ this._tail++;
10
+ }
11
+ dequeue() {
12
+ if (this._tail === this._head)
13
+ return undefined;
14
+ const element = this._elements[this._head];
15
+ delete this._elements[this._head];
16
+ this._head++;
17
+ return element;
18
+ }
19
+ peek() {
20
+ return this._elements[this._head];
21
+ }
22
+ isEmpty() {
23
+ return this._head === this._tail;
24
+ }
25
+ print() {
26
+ console.log(this._elements);
27
+ }
28
+ }
@@ -0,0 +1,15 @@
1
+ import { matchSorter } from "match-sorter";
2
+ export class Recommender {
3
+ constructor(variables, minSuggestionLen) {
4
+ this._minimumSuggestionLength = minSuggestionLen > 0 ? minSuggestionLen : 1;
5
+ this.variableList = variables;
6
+ }
7
+ getRecommendations(word) {
8
+ if (word.length < this._minimumSuggestionLength)
9
+ return [];
10
+ const recommendations = matchSorter(this.variableList, word);
11
+ if (recommendations.length === 0 || (recommendations.length === 1 && recommendations[0] === word))
12
+ return [];
13
+ return recommendations;
14
+ }
15
+ }
@@ -0,0 +1,20 @@
1
+ export class Stack {
2
+ constructor() {
3
+ this._elements = [];
4
+ }
5
+ push(item) {
6
+ this._elements.push(item);
7
+ }
8
+ pop() {
9
+ return this._elements.pop();
10
+ }
11
+ top() {
12
+ return this._elements.at(-1);
13
+ }
14
+ isEmpty() {
15
+ return this._elements.length === 0;
16
+ }
17
+ print() {
18
+ console.log(this._elements);
19
+ }
20
+ }
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@fw-components/formula-editor",
3
- "version": "2.0.7-formula-editor-enhancements.12",
3
+ "version": "2.0.7-formula-editor.21",
4
4
  "description": "A WYSIWYG type formula editor",
5
- "main": "dist/formula-editor/src/formula-builder.js",
5
+ "main": "dist/formula-editor/src/formula-editor.js",
6
+ "exports": {
7
+ ".": "./dist/formula-editor/src/formula-editor.js",
8
+ "./types": "./dist/formula-editor/src/types",
9
+ "./types/*.js": "./dist/formula-editor/src/types/*.js",
10
+ "./utils/*.js": "./dist/formula-editor/src/utils/*.js"
11
+ },
6
12
  "publishConfig": {
7
13
  "access": "public"
8
14
  },
@@ -25,5 +31,5 @@
25
31
  "@types/big.js": "^6.1.6",
26
32
  "es-dev-server": "^2.1.0"
27
33
  },
28
- "gitHead": "cae07cc0c50d572dec629f58e6099e354c9f6493"
34
+ "gitHead": "2ee961ac801dddf4861fa5af2fce28636ffd1d74"
29
35
  }
@@ -1,142 +0,0 @@
1
- export class Cursor {
2
- /**
3
- * The functions `getCurrentCursorPosition`, `setCurrentCursorPosition` and their
4
- * helpers `_createRange` and `_isChildOf` are not used for caret manipulation,
5
- * but are still in the code for future reference, if the functionality breaks
6
- * somehow in some obsolete browser.
7
- */
8
- static getCurrentCursorPosition(parentElement) {
9
- let selection = window.getSelection(), charCount = -1, node;
10
- if (selection?.focusNode) {
11
- if (Cursor._isChildOf(selection.focusNode, parentElement)) {
12
- node = selection.focusNode;
13
- charCount = selection.focusOffset;
14
- while (node) {
15
- if (node === parentElement) {
16
- break;
17
- }
18
- if (node.previousSibling) {
19
- node = node.previousSibling;
20
- charCount += node.textContent?.length ?? 0;
21
- }
22
- else {
23
- node = node.parentNode;
24
- if (node === null) {
25
- break;
26
- }
27
- }
28
- }
29
- }
30
- }
31
- return charCount;
32
- }
33
- static setCurrentCursorPosition(chars, element) {
34
- if (chars >= 0) {
35
- var selection = window.getSelection();
36
- let range = Cursor._createRange(element, { count: chars }, undefined);
37
- if (range) {
38
- range.collapse(false);
39
- selection?.removeAllRanges();
40
- selection?.addRange(range);
41
- }
42
- }
43
- }
44
- static _createRange(node, chars, range) {
45
- if (!range) {
46
- range = document.createRange();
47
- range.selectNode(node);
48
- range.setStart(node, 0);
49
- }
50
- if (chars.count === 0) {
51
- range.setEnd(node, chars.count);
52
- }
53
- else if (node && chars.count > 0) {
54
- if (node.nodeType === Node.TEXT_NODE) {
55
- if (node.textContent.length < chars.count) {
56
- chars.count -= node.textContent.length;
57
- }
58
- else {
59
- range.setEnd(node, chars.count);
60
- chars.count = 0;
61
- }
62
- }
63
- else {
64
- for (var lp = 0; lp < node.childNodes.length; lp++) {
65
- range = Cursor._createRange(node.childNodes[lp], chars, range);
66
- if (chars.count === 0) {
67
- break;
68
- }
69
- }
70
- }
71
- }
72
- return range;
73
- }
74
- static _isChildOf(node, parentElement) {
75
- while (node !== null) {
76
- if (node === parentElement) {
77
- return true;
78
- }
79
- node = node.parentNode;
80
- }
81
- return false;
82
- }
83
- static _getComposedRange(shadowRoot) {
84
- // `getSelection` is not defined for the type ShadowRoot in TS,
85
- // but it does exist.
86
- const sr = shadowRoot;
87
- if (sr.getSelection && typeof sr.getSelection === 'function') {
88
- const selection = sr.getSelection();
89
- if (selection && selection.rangeCount > 0) {
90
- return selection.getRangeAt(0);
91
- }
92
- }
93
- // Fallback for browsers like Firefox/Safari.
94
- const selection = window.getSelection();
95
- if (selection && selection.rangeCount > 0) {
96
- const range = selection.getRangeAt(0);
97
- if (shadowRoot.contains(range.startContainer)) {
98
- return range;
99
- }
100
- }
101
- return null;
102
- }
103
- static getCaretPosition(shadowRoot, element) {
104
- const range = Cursor._getComposedRange(shadowRoot);
105
- if (!range) {
106
- return 0;
107
- }
108
- const prefix = range.cloneRange();
109
- prefix.selectNodeContents(element);
110
- prefix.setEnd(range.endContainer, range.endOffset);
111
- return prefix.toString().length;
112
- }
113
- static { this.setCaretPosition = (pos, parent) => {
114
- for (const node of parent.childNodes) {
115
- if (node.nodeType == Node.TEXT_NODE) {
116
- if (node.length >= pos) {
117
- const range = document.createRange();
118
- const sel = window.getSelection();
119
- range.setStart(node, pos);
120
- range.collapse(true);
121
- sel.removeAllRanges();
122
- sel.addRange(range);
123
- return -1;
124
- }
125
- else {
126
- pos = pos - node.length;
127
- }
128
- }
129
- else {
130
- pos = this.setCaretPosition(pos, node);
131
- if (pos < 0) {
132
- return pos;
133
- }
134
- }
135
- }
136
- return pos;
137
- }; }
138
- static getCursorRect(shadowRoot) {
139
- const range = Cursor._getComposedRange(shadowRoot);
140
- return range ? range.getClientRects()[0] : undefined;
141
- }
142
- }