autopair 1.2.4 → 1.2.6

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.
package/README.md CHANGED
@@ -67,6 +67,10 @@ npm install autopair
67
67
 
68
68
  Report issues on the [Veritula issue tracker](https://veritula.com/discussions/autopair-js). Do not submit issues on GitHub.
69
69
 
70
+ ## Bug bounty program
71
+
72
+ There’s a [Veritula bug bounty program](https://veritula.com/bounties/10) for autopair.js. Report bugs there, not on GitHub.
73
+
70
74
  ## Development
71
75
 
72
76
  Run a webserver and open `index.html`.
@@ -17,7 +17,7 @@ describe('autopair', () => {
17
17
  // Check result
18
18
  cy.get('textarea').then($el => {
19
19
  expect($el.val()).to.eq('(hello)');
20
- expect($el[0].selectionStart).to.eq(6); // cursor after )
20
+ expect($el[0].selectionStart).to.eq(1);
21
21
  expect($el[0].selectionEnd).to.eq(6);
22
22
  });
23
23
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autopair",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Automatically closes parentheses and similar characters.",
5
5
  "main": "src/autopair.js",
6
6
  "author": "Dennis Hackethal <engineering@dennishackethal.com>",
package/src/autopair.js CHANGED
@@ -5,75 +5,67 @@ export default function autopair(textarea, pairs = {
5
5
  "'": "'",
6
6
  '"': '"'
7
7
  }) {
8
- let openings = Object.keys(pairs);
9
- let closings = Object.values(pairs);
8
+ const closingFor = new Map(Object.entries(pairs));
9
+ const openingFor = new Map(Object.entries(pairs).map(([k, v]) => [v, k]));
10
10
 
11
- let insertText = text => document.execCommand('insertText', false, text);
12
- let setCursor = pos => {
13
- textarea.selectionStart = textarea.selectionEnd = pos;
11
+ const insertText = text => document.execCommand('insertText', false, text);
12
+ const setSelection = (start, end) => {
13
+ textarea.selectionStart = start;
14
+ textarea.selectionEnd = end;
14
15
  };
15
16
 
16
- let handler = evt => {
17
- let { selectionStart: start, selectionEnd: end, value } = textarea;
17
+ const handler = evt => {
18
+ const { selectionStart: start, selectionEnd: end, value } = textarea;
18
19
 
19
- // Typethrough
20
- if (start === end && closings.includes(evt.key) && value[end] === evt.key) {
20
+ // Typethrough: move cursor past an existing closing char
21
+ if (start === end && openingFor.has(evt.key) && value[end] === evt.key) {
21
22
  evt.preventDefault();
22
- setCursor(end + 1);
23
+ setSelection(end + 1, end + 1);
24
+
23
25
  return;
24
26
  }
25
27
 
26
- // Handle backspace inside a direct pair
28
+ // Backspace inside a direct pair
27
29
  if (evt.key === 'Backspace' && start === end && start > 0) {
28
- let left = value[start - 1];
29
- let right = value[start];
30
- let opening = openings.find(k => pairs[k] === right);
30
+ const left = value[start - 1];
31
+ const right = value[start];
32
+ const opening = openingFor.get(right);
31
33
 
32
34
  if (left === opening) {
33
35
  evt.preventDefault();
34
-
35
- // Select the pair and delete in one go
36
- textarea.selectionStart = start - 1;
37
- textarea.selectionEnd = start + 1;
38
-
36
+ setSelection(start - 1, start + 1);
39
37
  insertText('');
40
38
 
41
39
  return;
42
40
  }
43
-
44
- return; // normal backspace
45
41
  }
46
42
 
47
- let closing = pairs[evt.key];
43
+ const closing = closingFor.get(evt.key);
48
44
  if (!closing) return;
49
45
 
50
- // Wrap selection if present
46
+ // Wrap selection
51
47
  if (start !== end) {
52
48
  evt.preventDefault();
53
- textarea.selectionStart = start;
54
- textarea.selectionEnd = end;
55
49
  insertText(evt.key + value.slice(start, end) + closing);
56
- setCursor(end + 1);
50
+ setSelection(start + 1, end + 1);
57
51
 
58
52
  return;
59
53
  }
60
54
 
61
- let nextCharWhitelist = /[\s;})\]]/;
62
- let nextChar = value[end] || '';
63
- let prevChar = value[start - 1] || '';
64
- let insidePair = closing === nextChar;
65
- let safeNext = nextChar === '' || nextCharWhitelist.test(nextChar);
66
- let isSymmetric = evt.key === closing;
55
+ // Autoclose single characters when typing next to a safe context
56
+ const nextChar = value[end] || '';
57
+ const prevChar = value[start - 1] || '';
58
+ const insidePair = closing === nextChar;
59
+ const safeNext = nextChar === '' || /[\s;})\]]/.test(nextChar);
60
+ const isSymmetric = evt.key === closing;
67
61
 
68
- // Autoclose only when allowed
69
- if (!insidePair && (!safeNext || (isSymmetric && (start !== end || prevChar === evt.key || /\w/.test(prevChar))))) {
62
+ if (!insidePair && (!safeNext || (isSymmetric && (prevChar === evt.key || /\w/.test(prevChar))))) {
70
63
  return;
71
64
  }
72
65
 
73
66
  evt.preventDefault();
74
-
75
67
  insertText(evt.key + closing);
76
- setCursor(start + 1);
68
+ setSelection(start + 1, start + 1);
77
69
  };
78
70
 
79
71
  textarea.addEventListener('keydown', handler);