@taufik-nurrohman/text-editor.source 2.2.11 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
3
+ Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the “Software”), to deal
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
- Text Editor Source
2
- ==================
1
+ [Text Editor](https://github.com/taufik-nurrohman/text-editor) &raquo; Source
2
+ =============================================================================
3
3
 
4
- > Source code editor extension for [text editor](https://github.com/taufik-nurrohman/text-editor).
4
+ Source extension for [Text Editor](https://github.com/taufik-nurrohman/text-editor). This extension provides a set of
5
+ key strokes to generate responses like in a typical source code editor.
5
6
 
6
- Provides a set of key strokes to generate responses like in a typical source code editor.
7
+ ![index.js](https://img.shields.io/github/size/taufik-nurrohman/text-editor.source/index.js?branch=main&color=%23f1e05a&label=index.js&labelColor=%231f2328&style=flat-square)
8
+ ![index.min.js](https://img.shields.io/github/size/taufik-nurrohman/text-editor.source/index.min.js?branch=main&color=%23f1e05a&label=index.min.js&labelColor=%231f2328&style=flat-square)
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * The MIT License (MIT)
4
4
  *
5
- * Copyright © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
5
+ * Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
6
6
  *
7
7
  * Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  * of this software and associated documentation files (the “Software”), to deal
@@ -24,26 +24,46 @@
24
24
  *
25
25
  */
26
26
  (function (g, f) {
27
- typeof exports === 'object' && typeof module !== 'undefined' ? f(exports) : typeof define === 'function' && define.amd ? define(['exports'], f) : (g = typeof globalThis !== 'undefined' ? globalThis : g || self, f((g.TE = g.TE || {}, g.TE.Source = {})));
28
- })(this, (function (exports) {
27
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = f() : typeof define === 'function' && define.amd ? define(f) : (g = typeof globalThis !== 'undefined' ? globalThis : g || self, (g.TextEditor = g.TextEditor || {}, g.TextEditor.Source = f()));
28
+ })(this, (function () {
29
29
  'use strict';
30
+ var debounce = function debounce(then, time) {
31
+ var timer;
32
+ return function () {
33
+ var _arguments = arguments,
34
+ _this = this;
35
+ timer && clearTimeout(timer);
36
+ timer = setTimeout(function () {
37
+ return then.apply(_this, _arguments);
38
+ }, time);
39
+ };
40
+ };
30
41
  var hasValue = function hasValue(x, data) {
31
42
  return -1 !== data.indexOf(x);
32
43
  };
33
44
  var isArray = function isArray(x) {
34
45
  return Array.isArray(x);
35
46
  };
36
- var isDefined = function isDefined(x) {
47
+ var isDefined$1 = function isDefined(x) {
37
48
  return 'undefined' !== typeof x;
38
49
  };
39
- var isInstance = function isInstance(x, of) {
40
- return x && isSet(of) && x instanceof of ;
50
+ var isInstance$1 = function isInstance(x, of) {
51
+ return x && isSet$1(of) && x instanceof of ;
41
52
  };
42
- var isNull = function isNull(x) {
53
+ var isNull$1 = function isNull(x) {
43
54
  return null === x;
44
55
  };
45
- var isSet = function isSet(x) {
46
- return isDefined(x) && !isNull(x);
56
+ var isObject = function isObject(x, isPlain) {
57
+ if (isPlain === void 0) {
58
+ isPlain = true;
59
+ }
60
+ if ('object' !== typeof x) {
61
+ return false;
62
+ }
63
+ return isPlain ? isInstance$1(x, Object) : true;
64
+ };
65
+ var isSet$1 = function isSet(x) {
66
+ return isDefined$1(x) && !isNull$1(x);
47
67
  };
48
68
  var isString = function isString(x) {
49
69
  return 'string' === typeof x;
@@ -54,17 +74,61 @@
54
74
  var toObjectValues = function toObjectValues(x) {
55
75
  return Object.values(x);
56
76
  };
57
- var W = window;
58
- var debounce = function debounce(then, time) {
59
- var timer;
60
- return function () {
61
- var _arguments = arguments,
62
- _this = this;
63
- timer && clearTimeout(timer);
64
- timer = setTimeout(function () {
65
- return then.apply(_this, _arguments);
66
- }, time);
67
- };
77
+ var fromStates = function fromStates() {
78
+ for (var _len = arguments.length, lot = new Array(_len), _key = 0; _key < _len; _key++) {
79
+ lot[_key] = arguments[_key];
80
+ }
81
+ var out = lot.shift();
82
+ for (var i = 0, j = toCount(lot); i < j; ++i) {
83
+ for (var k in lot[i]) {
84
+ // Assign value
85
+ if (!isSet$1(out[k])) {
86
+ out[k] = lot[i][k];
87
+ continue;
88
+ }
89
+ // Merge array
90
+ if (isArray(out[k]) && isArray(lot[i][k])) {
91
+ out[k] = [ /* Clone! */ ].concat(out[k]);
92
+ for (var ii = 0, jj = toCount(lot[i][k]); ii < jj; ++ii) {
93
+ if (!hasValue(lot[i][k][ii], out[k])) {
94
+ out[k].push(lot[i][k][ii]);
95
+ }
96
+ }
97
+ // Merge object recursive
98
+ } else if (isObject(out[k]) && isObject(lot[i][k])) {
99
+ out[k] = fromStates({
100
+ /* Clone! */ }, out[k], lot[i][k]);
101
+ // Replace value
102
+ } else {
103
+ out[k] = lot[i][k];
104
+ }
105
+ }
106
+ }
107
+ return out;
108
+ };
109
+ var offEvent = function offEvent(name, node, then) {
110
+ node.removeEventListener(name, then);
111
+ };
112
+ var offEventDefault = function offEventDefault(e) {
113
+ return e && e.preventDefault();
114
+ };
115
+ var onEvent = function onEvent(name, node, then, options) {
116
+ if (options === void 0) {
117
+ options = false;
118
+ }
119
+ node.addEventListener(name, then, options);
120
+ };
121
+ var isDefined = function isDefined(x) {
122
+ return 'undefined' !== typeof x;
123
+ };
124
+ var isInstance = function isInstance(x, of) {
125
+ return x && isSet(of) && x instanceof of ;
126
+ };
127
+ var isNull = function isNull(x) {
128
+ return null === x;
129
+ };
130
+ var isSet = function isSet(x) {
131
+ return isDefined(x) && !isNull(x);
68
132
  };
69
133
  var isPattern = function isPattern(pattern) {
70
134
  return isInstance(pattern, RegExp);
@@ -73,375 +137,277 @@
73
137
  if (isPattern(pattern)) {
74
138
  return pattern;
75
139
  }
76
- // No need to escape `/` in the pattern string
77
- pattern = pattern.replace(/\//g, '\\/');
78
140
  return new RegExp(pattern, isSet(opt) ? opt : 'g');
79
141
  };
80
- var pairs = {
81
- '`': '`',
82
- '(': ')',
83
- '{': '}',
84
- '[': ']',
85
- '"': '"',
86
- "'": "'",
87
- '<': '>'
88
- };
89
-
90
- function promisify(type, lot) {
91
- return new Promise(function (resolve, reject) {
92
- var r = W[type].apply(W, lot);
93
- return r ? resolve(r) : reject(r);
94
- });
95
- }
96
- var defaults = {
97
- source: {
98
- pairs: pairs,
99
- type: null
100
- }
101
- };
102
- ['alert', 'confirm', 'prompt'].forEach(function (type) {
103
- defaults.source[type] = function () {
104
- for (var _len = arguments.length, lot = new Array(_len), _key = 0; _key < _len; _key++) {
105
- lot[_key] = arguments[_key];
106
- }
107
- return promisify(type, lot);
108
- };
109
- });
110
- var that = {};
111
- that.toggle = function (open, close, wrap, tidy) {
112
- if (tidy === void 0) {
113
- tidy = false;
114
- }
115
- if (!close && "" !== close) {
116
- close = open;
117
- }
118
- var t = this,
119
- _t$$ = t.$(),
120
- after = _t$$.after,
121
- before = _t$$.before,
122
- value = _t$$.value,
123
- closeCount = toCount(close),
124
- openCount = toCount(open);
125
- if (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount) || close === after.slice(0, closeCount) && open === before.slice(-openCount)) {
126
- return t.peel(open, close, wrap);
127
- }
128
- if (false !== tidy) {
129
- if (isString(tidy)) {
130
- tidy = [tidy, tidy];
131
- } else if (!isArray(tidy)) {
132
- tidy = ["", ""];
133
- }
134
- if (!isSet(tidy[1])) {
135
- tidy[1] = tidy[0];
136
- }
137
- t.trim(tidy[0], tidy[1]);
138
- }
139
- return t.wrap(open, close, wrap);
140
- };
142
+ var ALT_PREFIX = 'Alt-';
141
143
  var CTRL_PREFIX = 'Control-';
142
144
  var SHIFT_PREFIX = 'Shift-';
145
+ var bounce = debounce(function ($) {
146
+ return $.record();
147
+ }, 10);
143
148
 
144
- function canKeyDown(map, of) {
149
+ function onKeyDown(e) {
150
+ var _editor$state$source, _editor$state$source2;
151
+ var self = this,
152
+ editor = self.TextEditor,
153
+ keys = editor.k();
154
+ if (!editor || e.defaultPrevented) {
155
+ return;
156
+ }
157
+ bounce(editor);
158
+ if (editor.keys[keys]) {
159
+ return;
160
+ }
145
161
  var charAfter,
146
162
  charBefore,
147
- charIndent = of.state.source.tab || of.state.tab || '\t',
148
- charPairs = of.state.source.pairs || {},
149
- charPairsValues = toObjectValues(charPairs),
150
- key = map.key,
151
- queue = map.queue,
152
- keyValue = map + "";
153
- // Do nothing
154
- if (queue.Alt || queue.Control) {
155
- return true;
163
+ charIndent = ((_editor$state$source = editor.state.source) == null ? void 0 : _editor$state$source.tab) || editor.state.tab || '\t',
164
+ charPairs = ((_editor$state$source2 = editor.state.source) == null ? void 0 : _editor$state$source2.pairs) || {},
165
+ charPairsValues = toObjectValues(charPairs);
166
+ var _editor$$ = editor.$(),
167
+ after = _editor$$.after,
168
+ before = _editor$$.before,
169
+ end = _editor$$.end,
170
+ start = _editor$$.start,
171
+ value = _editor$$.value,
172
+ lineAfter = after.split('\n').shift(),
173
+ lineBefore = before.split('\n').pop(),
174
+ lineMatch = lineBefore.match(/^(\s+)/),
175
+ lineMatchIndent = lineMatch && lineMatch[1] || "";
176
+ if (CTRL_PREFIX + SHIFT_PREFIX + 'Enter' === keys) {
177
+ if (before || after) {
178
+ // Insert line above with `⎈⇧↵`
179
+ offEventDefault(e);
180
+ return editor.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
181
+ }
182
+ return;
156
183
  }
157
- if (' ' === keyValue) {
158
- var _of$$ = of.$(),
159
- _after = _of$$.after,
160
- _before = _of$$.before,
161
- _value = _of$$.value;
162
- charAfter = charPairs[charBefore = _before.slice(-1)];
163
- if (!_value && charAfter && charBefore && charAfter === _after[0]) {
164
- of.wrap(' ', ' ');
165
- return false;
184
+ if (CTRL_PREFIX + 'Enter' === keys) {
185
+ if (before || after) {
186
+ // Insert line below with `⎈↵`
187
+ offEventDefault(e);
188
+ return editor.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
166
189
  }
167
- return true;
168
190
  }
169
- if ('Enter' === keyValue || SHIFT_PREFIX + 'Enter' === keyValue) {
170
- var _of$$2 = of.$(),
171
- _after2 = _of$$2.after,
172
- _before2 = _of$$2.before,
173
- _value2 = _of$$2.value,
174
- lineBefore = _before2.split('\n').pop(),
175
- lineMatch = lineBefore.match(/^(\s+)/),
176
- lineMatchIndent = lineMatch && lineMatch[1] || "";
177
- if (!_value2) {
178
- if (_after2 && _before2 && (charAfter = charPairs[charBefore = _before2.slice(-1)]) && charAfter === _after2[0]) {
179
- of.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
180
- return false;
181
- }
182
- if (lineMatchIndent) {
183
- of.insert('\n' + lineMatchIndent, -1).record();
184
- return false;
185
- }
191
+ // Do nothing
192
+ if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
193
+ offEventDefault(e);
194
+ return;
195
+ }
196
+ if (' ' === keys) {
197
+ charAfter = charPairs[charBefore = before.slice(-1)];
198
+ if (!value && charAfter && charBefore && charAfter === after[0]) {
199
+ offEventDefault(e);
200
+ return editor.wrap(' ', ' ');
186
201
  }
187
- return true;
202
+ return;
188
203
  }
189
- if ('Backspace' === keyValue) {
190
- var _of$$3 = of.$(),
191
- _after3 = _of$$3.after,
192
- _before3 = _of$$3.before,
193
- _value3 = _of$$3.value;
194
- _after3.split('\n')[0];
195
- var _lineBefore = _before3.split('\n').pop(),
196
- _lineMatch = _lineBefore.match(/^(\s+)/),
197
- _lineMatchIndent = _lineMatch && _lineMatch[1] || "";
198
- charAfter = charPairs[charBefore = _before3.slice(-1)];
204
+ if ('Backspace' === keys) {
205
+ charAfter = charPairs[charBefore = before.slice(-1)];
199
206
  // Do nothing on escape
200
207
  if ('\\' === charBefore) {
201
- return true;
208
+ return;
202
209
  }
203
- if (_value3) {
204
- if (_after3 && _before3 && charAfter && charAfter === _after3[0] && !_before3.endsWith('\\' + charBefore)) {
205
- of.record().peel(charBefore, charAfter).record();
206
- return false;
210
+ if (value) {
211
+ if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
212
+ offEventDefault(e);
213
+ return editor.record().peel(charBefore, charAfter).record();
207
214
  }
208
- return true;
215
+ return;
209
216
  }
210
- charAfter = charPairs[charBefore = _before3.trim().slice(-1)];
217
+ charAfter = charPairs[charBefore = before.trim().slice(-1)];
211
218
  if (charAfter && charBefore) {
212
- if (_after3.startsWith(' ' + charAfter) && _before3.endsWith(charBefore + ' ') || _after3.startsWith('\n' + _lineMatchIndent + charAfter) && _before3.endsWith(charBefore + '\n' + _lineMatchIndent)) {
219
+ if (after.startsWith(' ' + charAfter) && before.endsWith(charBefore + ' ') || after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)) {
213
220
  // Collapse bracket(s)
214
- of.trim("", "").record();
215
- return false;
221
+ offEventDefault(e);
222
+ return editor.trim("", "").record();
216
223
  }
217
224
  }
218
225
  // Outdent
219
- if (_lineBefore.endsWith(charIndent)) {
220
- of.pull(charIndent).record();
221
- return false;
226
+ if (lineBefore.endsWith(charIndent)) {
227
+ offEventDefault(e);
228
+ return editor.pull(charIndent).record();
222
229
  }
223
- if (_after3 && _before3 && !_before3.endsWith('\\' + charBefore)) {
224
- if (charAfter === _after3[0] && charBefore === _before3.slice(-1)) {
230
+ if (after && before && !before.endsWith('\\' + charBefore)) {
231
+ if (charAfter === after[0] && charBefore === before.slice(-1)) {
225
232
  // Peel pair
226
- of.peel(charBefore, charAfter).record();
227
- return false;
233
+ offEventDefault(e);
234
+ return editor.peel(charBefore, charAfter).record();
228
235
  }
229
236
  }
230
- return true;
237
+ return;
238
+ }
239
+ if ('Enter' === keys || SHIFT_PREFIX + 'Enter' === keys) {
240
+ if (!value) {
241
+ if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
242
+ offEventDefault(e);
243
+ return editor.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
244
+ }
245
+ if (lineMatchIndent) {
246
+ offEventDefault(e);
247
+ return editor.insert('\n' + lineMatchIndent, -1).record();
248
+ }
249
+ }
250
+ return;
231
251
  }
232
- var _of$$4 = of.$(),
233
- after = _of$$4.after,
234
- before = _of$$4.before,
235
- start = _of$$4.start,
236
- value = _of$$4.value;
237
252
  // Do nothing on escape
238
253
  if ('\\' === (charBefore = before.slice(-1))) {
239
- return true;
254
+ return;
240
255
  }
241
256
  charAfter = hasValue(after[0], charPairsValues) ? after[0] : charPairs[charBefore];
257
+ keys = keys.replace(SHIFT_PREFIX, "");
242
258
  // `|}`
243
- if (!value && after && before && charAfter && key === charAfter) {
259
+ if (!value && after && before && charAfter && keys === charAfter) {
244
260
  // Move to the next character
245
261
  // `}|`
246
- of.select(start + 1).record();
247
- return false;
262
+ offEventDefault(e);
263
+ return editor.select(start + 1).record();
248
264
  }
249
265
  for (charBefore in charPairs) {
250
266
  charAfter = charPairs[charBefore];
251
267
  // `{|`
252
- if (key === charBefore && charAfter) {
268
+ if (keys === charBefore && charAfter) {
253
269
  // Wrap pair or selection
254
270
  // `{|}` `{|aaa|}`
255
- of.wrap(charBefore, charAfter).record();
256
- return false;
271
+ offEventDefault(e);
272
+ return editor.wrap(charBefore, charAfter).record();
257
273
  }
258
274
  // `|}`
259
- if (key === charAfter) {
275
+ if (keys === charAfter) {
260
276
  if (value) {
261
277
  // Wrap selection
262
278
  // `{|aaa|}`
263
- of.record().wrap(charBefore, charAfter).record();
264
- return false;
279
+ offEventDefault(e);
280
+ return editor.record().wrap(charBefore, charAfter).record();
265
281
  }
266
282
  break;
267
283
  }
268
284
  }
269
- return true;
270
- }
271
-
272
- function canKeyDownDent(map, of) {
273
- var charIndent = of.state.source.tab || of.state.tab || '\t';
274
- map.key;
275
- map.queue;
276
- var keyValue = map + "";
277
- // Indent with `⎈]`
278
- if (CTRL_PREFIX + ']' === keyValue) {
279
- of.push(charIndent).record();
280
- return false;
281
- }
282
- // Outdent with `⎈[`
283
- if (CTRL_PREFIX + '[' === keyValue) {
284
- of.pull(charIndent).record();
285
- return false;
286
- }
287
- return true;
288
- }
289
-
290
- function canKeyDownEnter(map, of) {
291
- map.key;
292
- var queue = map.queue;
293
- if (queue.Control && queue.Enter) {
294
- var _of$$5 = of.$(),
295
- after = _of$$5.after,
296
- before = _of$$5.before,
297
- end = _of$$5.end,
298
- start = _of$$5.start,
299
- value = _of$$5.value,
300
- lineAfter = after.split('\n').shift(),
301
- lineBefore = before.split('\n').pop(),
302
- lineMatch = lineBefore.match(/^(\s+)/),
303
- lineMatchIndent = lineMatch && lineMatch[1] || "";
304
- if (before || after) {
305
- if (queue.Shift) {
306
- // Insert line above with `⎈⇧↵`
307
- return of.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
308
- }
309
- // Insert line below with `⎈↵`
310
- return of.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
311
- }
312
- }
313
- return true;
314
- }
315
-
316
- function canKeyDownHistory(map, of) {
317
- var keyValue = map + "";
318
- // Redo with `⎈y`
319
- if (CTRL_PREFIX + 'y' === keyValue) {
320
- return of.redo(), false;
321
- }
322
- // Undo with `⎈z`
323
- if (CTRL_PREFIX + 'z' === keyValue) {
324
- return of.undo(), false;
325
- }
326
- return true;
327
- }
328
-
329
- function canKeyDownMove(map, of) {
330
- map.key;
331
- var queue = map.queue,
332
- keyValue = map + "";
333
- if (!queue.Control) {
334
- return true;
335
- }
336
- var _of$$6 = of.$(),
337
- after = _of$$6.after,
338
- before = _of$$6.before,
339
- end = _of$$6.end,
340
- start = _of$$6.start,
341
- value = _of$$6.value,
342
- charPair,
285
+ var charPair,
343
286
  charPairValue,
344
- charPairs = of.state.source.pairs || {},
345
- boundaries = [],
346
- m;
287
+ m,
288
+ tokens = [];
347
289
  if (value) {
348
290
  for (charPair in charPairs) {
349
291
  if (!(charPairValue = charPairs[charPair])) {
350
292
  continue;
351
293
  }
352
- boundaries.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
294
+ tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
353
295
  }
354
- boundaries.push('\\w+'); // Word(s)
355
- boundaries.push('\\s+'); // White-space(s)
356
- boundaries.push('[\\s\\S]'); // Last try!
357
- if (CTRL_PREFIX + 'ArrowLeft' === keyValue) {
358
- if (m = before.match(toPattern('(' + boundaries.join('|') + ')$', ""))) {
359
- of.insert("").select(start - toCount(m[0])).insert(value);
360
- return of.record(), false;
296
+ tokens.push('\\w+'); // Word(s)
297
+ tokens.push('\\s+'); // White-space(s)
298
+ tokens.push('[\\s\\S]'); // Last try!
299
+ if (CTRL_PREFIX + 'ArrowLeft' === keys) {
300
+ offEventDefault(e);
301
+ if (m = before.match(toPattern('(' + tokens.join('|') + ')$', ""))) {
302
+ return editor.insert("").select(start - toCount(m[0])).insert(value).record();
361
303
  }
362
- return of.select(), false;
304
+ return editor.select();
363
305
  }
364
- if (CTRL_PREFIX + 'ArrowRight' === keyValue) {
365
- if (m = after.match(toPattern('^(' + boundaries.join('|') + ')', ""))) {
366
- of.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value);
367
- return of.record(), false;
306
+ if (CTRL_PREFIX + 'ArrowRight' === keys) {
307
+ offEventDefault(e);
308
+ if (m = after.match(toPattern('^(' + tokens.join('|') + ')', ""))) {
309
+ return editor.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value).record();
368
310
  }
369
- return of.select(), false;
311
+ return editor.select();
370
312
  }
371
313
  }
372
- var lineAfter = after.split('\n').shift(),
373
- lineBefore = before.split('\n').pop(),
374
- lineMatch = lineBefore.match(/^(\s+)/);
375
- lineMatch && lineMatch[1] || "";
376
314
  // Force to select the current line if there is no selection
377
315
  end += toCount(lineAfter);
378
316
  start -= toCount(lineBefore);
379
317
  value = lineBefore + value + lineAfter;
380
- if (CTRL_PREFIX + 'ArrowUp' === keyValue) {
318
+ if (CTRL_PREFIX + 'ArrowUp' === keys) {
319
+ offEventDefault(e);
381
320
  if (!hasValue('\n', before)) {
382
- return of.select(), false;
321
+ return editor.select();
383
322
  }
384
- of.insert("");
385
- of.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
386
- of.replace(/(^|\n)([^\n]*?)$/, "", -1);
387
- var $ = of.$();
323
+ editor.insert("");
324
+ editor.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
325
+ editor.replace(/(^|\n)([^\n]*?)$/, "", -1);
326
+ var $ = editor.$();
388
327
  before = $.before;
389
328
  start = $.start;
390
329
  lineBefore = before.split('\n').pop();
391
- of.select(start = start - toCount(lineBefore)).wrap(value, '\n');
392
- of.select(start, start + toCount(value));
393
- return of.record(), false;
330
+ editor.select(start = start - toCount(lineBefore)).wrap(value, '\n');
331
+ editor.select(start, start + toCount(value));
332
+ return editor.record();
394
333
  }
395
- if (CTRL_PREFIX + 'ArrowDown' === keyValue) {
334
+ if (CTRL_PREFIX + 'ArrowDown' === keys) {
335
+ offEventDefault(e);
396
336
  if (!hasValue('\n', after)) {
397
- return of.select(), false;
337
+ return editor.select();
398
338
  }
399
- of.insert("");
400
- of.replace(/^([^\n]*?)(\n|$)/, "", 1);
401
- of.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
402
- var _$ = of.$();
339
+ editor.insert("");
340
+ editor.replace(/^([^\n]*?)(\n|$)/, "", 1);
341
+ editor.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
342
+ var _$ = editor.$();
403
343
  after = _$.after;
404
344
  end = _$.end;
405
345
  lineAfter = after.split('\n').shift();
406
- of.select(end = end + toCount(lineAfter)).wrap('\n', value);
346
+ editor.select(end = end + toCount(lineAfter)).wrap('\n', value);
407
347
  end += 1;
408
- of.select(end, end + toCount(value));
409
- return of.record(), false;
348
+ editor.select(end, end + toCount(value));
349
+ return editor.record();
410
350
  }
411
- return true;
351
+ return;
412
352
  }
413
353
 
414
- function canKeyDownTab(map, of) {
415
- var charIndent = of.state.source.tab || of.state.tab || '\t',
416
- keyValue = map + "";
417
- // Indent with `⇥`
418
- if ('Tab' === keyValue) {
419
- return of.push(charIndent).record(), false;
420
- }
421
- // Outdent with `⇧+⇥`
422
- if (SHIFT_PREFIX + 'Tab' === keyValue) {
423
- return of.pull(charIndent).record(), false;
424
- }
425
- return true;
354
+ function attach(self) {
355
+ var $ = this;
356
+ $.state = fromStates({
357
+ source: {
358
+ pairs: {
359
+ '`': '`',
360
+ '(': ')',
361
+ '{': '}',
362
+ '[': ']',
363
+ '"': '"',
364
+ "'": "'",
365
+ '<': '>'
366
+ },
367
+ type: null
368
+ }
369
+ }, $.state);
370
+ $.toggle = function (open, close, wrap, tidy) {
371
+ if (tidy === void 0) {
372
+ tidy = false;
373
+ }
374
+ if (!close && "" !== close) {
375
+ close = open;
376
+ }
377
+ var _$$$ = $.$(),
378
+ after = _$$$.after,
379
+ before = _$$$.before,
380
+ value = _$$$.value,
381
+ closeCount = toCount(close),
382
+ openCount = toCount(open);
383
+ if (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount) || close === after.slice(0, closeCount) && open === before.slice(-openCount)) {
384
+ return $.peel(open, close, wrap);
385
+ }
386
+ if (false !== tidy) {
387
+ if (isString(tidy)) {
388
+ tidy = [tidy, tidy];
389
+ } else if (!isArray(tidy)) {
390
+ tidy = ["", ""];
391
+ }
392
+ if (!isSet$1(tidy[1])) {
393
+ tidy[1] = tidy[0];
394
+ }
395
+ $.trim(tidy[0], tidy[1]);
396
+ }
397
+ return $.wrap(open, close, wrap);
398
+ };
399
+ onEvent('keydown', self, onKeyDown);
400
+ return $.record();
426
401
  }
427
- var bounce = debounce(function (of) {
428
- return of.record();
429
- }, 100);
430
402
 
431
- function canKeyUp(map, of) {
432
- return bounce(of), true;
403
+ function detach(self) {
404
+ var $ = this;
405
+ offEvent('keydown', self, onKeyDown);
406
+ return $;
433
407
  }
434
- var state = defaults;
435
- exports.canKeyDown = canKeyDown;
436
- exports.canKeyDownDent = canKeyDownDent;
437
- exports.canKeyDownEnter = canKeyDownEnter;
438
- exports.canKeyDownHistory = canKeyDownHistory;
439
- exports.canKeyDownMove = canKeyDownMove;
440
- exports.canKeyDownTab = canKeyDownTab;
441
- exports.canKeyUp = canKeyUp;
442
- exports.state = state;
443
- exports.that = that;
444
- Object.defineProperty(exports, '__esModule', {
445
- value: true
446
- });
408
+ var index_js = {
409
+ attach: attach,
410
+ detach: detach
411
+ };
412
+ return index_js;
447
413
  }));
package/index.min.js CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * The MIT License (MIT)
4
4
  *
5
- * Copyright © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
5
+ * Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
6
6
  *
7
7
  * Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  * of this software and associated documentation files (the “Software”), to deal
@@ -23,4 +23,4 @@
23
23
  * SOFTWARE.
24
24
  *
25
25
  */
26
- !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(((e="undefined"!=typeof globalThis?globalThis:e||self).TE=e.TE||{},e.TE.Source={}))}(this,(function(e){"use strict";var r=function(e,r){return-1!==r.indexOf(e)},t=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},n=function(e){return e.length},i=window,o=function(e){return n=RegExp,(r=e)&&t(n)&&r instanceof n;var r,n},a=function(e,r){return o(e)?e:(e=e.replace(/\//g,"\\/"),RegExp(e,t(r)?r:"g"))};function u(e,r){return new Promise((function(t,n){var o=i[e].apply(i,r);return o?t(o):n(o)}))}var s={source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"},type:null}};["alert","confirm","prompt"].forEach((function(e){s.source[e]=function(){for(var r=arguments.length,t=Array(r),n=0;n<r;n++)t[n]=arguments[n];return u(e,t)}}));var c={};c.toggle=function(e,r,i,o){void 0===o&&(o=!1),r||""===r||(r=e);var a=this,u=a.$(),s=u.after,c=u.before,f=u.value,l=n(r),p=n(e);return i&&r===f.slice(-l)&&e===f.slice(0,p)||r===s.slice(0,l)&&e===c.slice(-p)?a.peel(e,r,i):(!1!==o&&("string"==typeof o?o=[o,o]:function(e){return Array.isArray(e)}(o)||(o=["",""]),t(o[1])||(o[1]=o[0]),a.trim(o[0],o[1])),a.wrap(e,r,i))};var f="Control-",l="Shift-";var p,d,v,h=(p=function(e){return e.record()},d=100,function(){var e=arguments,r=this;v&&clearTimeout(v),v=setTimeout((function(){return p.apply(r,e)}),d)});var y=s;e.canKeyDown=function(e,t){var n,i,o=t.state.source.tab||t.state.tab||"\t",a=t.state.source.pairs||{},u=Object.values(a),s=e.key,c=e.queue,f=e+"";if(c.Alt||c.Control)return!0;if(" "===f){var p=t.$(),d=p.after,v=p.before,h=p.value;return n=a[i=v.slice(-1)],!(!h&&n&&i&&n===d[0])||(t.wrap(" "," "),!1)}if("Enter"===f||l+"Enter"===f){var y=t.$(),w=y.after,b=y.before,m=y.value,$=b.split("\n").pop().match(/^(\s+)/),g=$&&$[1]||"";if(!m){if(w&&b&&(n=a[i=b.slice(-1)])&&n===w[0])return t.wrap("\n"+g+(i!==n?o:""),"\n"+g).record(),!1;if(g)return t.insert("\n"+g,-1).record(),!1}return!0}if("Backspace"===f){var E=t.$(),T=E.after,A=E.before,D=E.value;T.split("\n")[0];var K=A.split("\n").pop(),W=K.match(/^(\s+)/),k=W&&W[1]||"";return n=a[i=A.slice(-1)],"\\"===i?!0:D?!(T&&A&&n&&n===T[0]&&!A.endsWith("\\"+i))||(t.record().peel(i,n).record(),!1):(n=a[i=A.trim().slice(-1)])&&i&&(T.startsWith(" "+n)&&A.endsWith(i+" ")||T.startsWith("\n"+k+n)&&A.endsWith(i+"\n"+k))?(t.trim("","").record(),!1):K.endsWith(o)?(t.pull(o).record(),!1):!(T&&A&&!A.endsWith("\\"+i)&&n===T[0]&&i===A.slice(-1))||(t.peel(i,n).record(),!1)}var x=t.$(),j=x.after,q=x.before,C=x.start,S=x.value;if("\\"===(i=q.slice(-1)))return!0;if(n=r(j[0],u)?j[0]:a[i],!S&&j&&q&&n&&s===n)return t.select(C+1).record(),!1;for(i in a){if(n=a[i],s===i&&n)return t.wrap(i,n).record(),!1;if(s===n){if(S)return t.record().wrap(i,n).record(),!1;break}}return!0},e.canKeyDownDent=function(e,r){var t=r.state.source.tab||r.state.tab||"\t";e.key,e.queue;var n=e+"";return f+"]"===n?(r.push(t).record(),!1):f+"["!==n||(r.pull(t).record(),!1)},e.canKeyDownEnter=function(e,r){e.key;var t=e.queue;if(t.Control&&t.Enter){var i=r.$(),o=i.after,a=i.before,u=i.end,s=i.start,c=i.value,f=o.split("\n").shift(),l=a.split("\n").pop(),p=l.match(/^(\s+)/),d=p&&p[1]||"";if(a||o)return t.Shift?(r.select(s-n(l)).wrap(d,"\n").insert(c).record(),!1):(r.select(u+n(f)).wrap("\n"+d,"").insert(c).record(),!1)}return!0},e.canKeyDownHistory=function(e,r){var t=e+"";return f+"y"===t?(r.redo(),!1):f+"z"!==t||(r.undo(),!1)},e.canKeyDownMove=function(e,t){e.key;var i=e+"";if(!e.queue.Control)return!0;var o,u,s,c=t.$(),l=c.after,p=c.before,d=c.end,v=c.start,h=c.value,y=t.state.source.pairs||{},w=[];if(h){for(o in y)(u=y[o])&&w.push("(?:\\"+o+"(?:\\\\.|[^\\"+o+(u!==o?"\\"+u:"")+"])*\\"+u+")");if(w.push("\\w+"),w.push("\\s+"),w.push("[\\s\\S]"),f+"ArrowLeft"===i)return(s=p.match(a("("+w.join("|")+")$","")))?(t.insert("").select(v-n(s[0])).insert(h),t.record(),!1):(t.select(),!1);if(f+"ArrowRight"===i)return(s=l.match(a("^("+w.join("|")+")","")))?(t.insert("").select(d+n(s[0])-n(h)).insert(h),t.record(),!1):(t.select(),!1)}var b=l.split("\n").shift(),m=p.split("\n").pop(),$=m.match(/^(\s+)/);if($&&$[1],d+=n(b),v-=n(m),h=m+h+b,f+"ArrowUp"===i){if(!r("\n",p))return t.select(),!1;t.insert(""),t.replace(/^([^\n]*?)(\n|$)/,"$2",1),t.replace(/(^|\n)([^\n]*?)$/,"",-1);var g=t.$();return p=g.before,v=g.start,m=p.split("\n").pop(),t.select(v-=n(m)).wrap(h,"\n"),t.select(v,v+n(h)),t.record(),!1}if(f+"ArrowDown"===i){if(!r("\n",l))return t.select(),!1;t.insert(""),t.replace(/^([^\n]*?)(\n|$)/,"",1),t.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var E=t.$();return l=E.after,d=E.end,b=l.split("\n").shift(),t.select(d+=n(b)).wrap("\n",h),d+=1,t.select(d,d+n(h)),t.record(),!1}return!0},e.canKeyDownTab=function(e,r){var t=r.state.source.tab||r.state.tab||"\t",n=e+"";return"Tab"===n?(r.push(t).record(),!1):l+"Tab"!==n||(r.pull(t).record(),!1)},e.canKeyUp=function(e,r){return h(r),!0},e.state=y,e.that=c,Object.defineProperty(e,"__esModule",{value:!0})}));
26
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):((e="undefined"!=typeof globalThis?globalThis:e||self).TextEditor=e.TextEditor||{},e.TextEditor.Source=r())}(this,(function(){"use strict";var e,r,t,n=function(e,r){return-1!==r.indexOf(e)},i=function(e){return Array.isArray(e)},o=function(e,r){return void 0===r&&(r=!0),"object"==typeof e&&(!r||function(e,r){return e&&c(r)&&e instanceof r}(e,Object))},c=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},s=function(e){return e.length},u=function e(){for(var r=arguments.length,t=Array(r),u=0;u<r;u++)t[u]=arguments[u];for(var f=t.shift(),a=0,l=s(t);a<l;++a)for(var d in t[a])if(c(f[d]))if(i(f[d])&&i(t[a][d])){f[d]=[].concat(f[d]);for(var p=0,v=s(t[a][d]);p<v;++p)n(t[a][d][p],f[d])||f[d].push(t[a][d][p])}else o(f[d])&&o(t[a][d])?f[d]=e({},f[d],t[a][d]):f[d]=t[a][d];else f[d]=t[a][d];return f},f=function(e){return e&&e.preventDefault()},a=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},l=function(e){return t=RegExp,(r=e)&&a(t)&&r instanceof t;var r,t},d=function(e,r){return l(e)?e:RegExp(e,a(r)?r:"g")},p="Control-",v="Shift-",h=(e=function(e){return e.record()},r=10,function(){var n=arguments,i=this;t&&clearTimeout(t),t=setTimeout((function(){return e.apply(i,n)}),r)});function w(e){var r,t,i=this.TextEditor,o=i.k();if(i&&!e.defaultPrevented&&(h(i),!i.keys[o])){var c,u,a=(null==(r=i.state.source)?void 0:r.tab)||i.state.tab||"\t",l=(null==(t=i.state.source)?void 0:t.pairs)||{},w=Object.values(l),y=i.$(),b=y.after,m=y.before,E=y.end,g=y.start,$=y.value,x=b.split("\n").shift(),A=m.split("\n").pop(),T=A.match(/^(\s+)/),W=T&&T[1]||"";if(p+v+"Enter"===o)return m||b?(f(e),i.select(g-s(A)).wrap(W,"\n").insert($).record(),!1):void 0;if(p+"Enter"===o&&(m||b))return f(e),i.select(E+s(x)).wrap("\n"+W,"").insert($).record(),!1;if("Alt-"!=o+"-"&&p!==o+"-"){if(" "===o)return c=l[u=m.slice(-1)],!$&&c&&u&&c===b[0]?(f(e),i.wrap(" "," ")):void 0;if("Backspace"===o){if(c=l[u=m.slice(-1)],"\\"===u)return;return $?b&&m&&c&&c===b[0]&&!m.endsWith("\\"+u)?(f(e),i.record().peel(u,c).record()):void 0:(c=l[u=m.trim().slice(-1)])&&u&&(b.startsWith(" "+c)&&m.endsWith(u+" ")||b.startsWith("\n"+W+c)&&m.endsWith(u+"\n"+W))?(f(e),i.trim("","").record()):A.endsWith(a)?(f(e),i.pull(a).record()):b&&m&&!m.endsWith("\\"+u)&&c===b[0]&&u===m.slice(-1)?(f(e),i.peel(u,c).record()):void 0}if("Enter"!==o&&v+"Enter"!==o){if("\\"!==(u=m.slice(-1))){if(c=n(b[0],w)?b[0]:l[u],o=o.replace(v,""),!$&&b&&m&&c&&o===c)return f(e),i.select(g+1).record();for(u in l){if(c=l[u],o===u&&c)return f(e),i.wrap(u,c).record();if(o===c){if($)return f(e),i.record().wrap(u,c).record();break}}var j,k,L,O=[];if($){for(j in l)(k=l[j])&&O.push("(?:\\"+j+"(?:\\\\.|[^\\"+j+(k!==j?"\\"+k:"")+"])*\\"+k+")");if(O.push("\\w+"),O.push("\\s+"),O.push("[\\s\\S]"),p+"ArrowLeft"===o)return f(e),(L=m.match(d("("+O.join("|")+")$","")))?i.insert("").select(g-s(L[0])).insert($).record():i.select();if(p+"ArrowRight"===o)return f(e),(L=b.match(d("^("+O.join("|")+")","")))?i.insert("").select(E+s(L[0])-s($)).insert($).record():i.select()}if(E+=s(x),g-=s(A),$=A+$+x,p+"ArrowUp"===o){if(f(e),!n("\n",m))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"$2",1),i.replace(/(^|\n)([^\n]*?)$/,"",-1);var R=i.$();return m=R.before,g=R.start,A=m.split("\n").pop(),i.select(g-=s(A)).wrap($,"\n"),i.select(g,g+s($)),i.record()}if(p+"ArrowDown"===o){if(f(e),!n("\n",b))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"",1),i.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var S=i.$();return b=S.after,E=S.end,x=b.split("\n").shift(),i.select(E+=s(x)).wrap("\n",$),E+=1,i.select(E,E+s($)),i.record()}}}else if(!$){if(b&&m&&(c=l[u=m.slice(-1)])&&c===b[0])return f(e),i.wrap("\n"+W+(u!==c?a:""),"\n"+W).record();if(W)return f(e),i.insert("\n"+W,-1).record()}}else f(e)}}var y={attach:function(e){var r=this;return r.state=u({source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"},type:null}},r.state),r.toggle=function(e,t,n,o){void 0===o&&(o=!1),t||""===t||(t=e);var u=r.$(),f=u.after,a=u.before,l=u.value,d=s(t),p=s(e);return n&&t===l.slice(-d)&&e===l.slice(0,p)||t===f.slice(0,d)&&e===a.slice(-p)?r.peel(e,t,n):(!1!==o&&("string"==typeof o?o=[o,o]:i(o)||(o=["",""]),c(o[1])||(o[1]=o[0]),r.trim(o[0],o[1])),r.wrap(e,t,n))},function(e,r,t,n){void 0===n&&(n=!1),r.addEventListener(e,t,n)}("keydown",e,w),r.record()},detach:function(e){return function(e,r,t){r.removeEventListener(e,t)}("keydown",e,w),this}};return y}));
package/index.mjs CHANGED
@@ -1,127 +1,78 @@
1
- import {W} from '@taufik-nurrohman/document';
2
1
  import {debounce} from '@taufik-nurrohman/tick';
3
- import {esc, escChar, toPattern} from '@taufik-nurrohman/pattern';
2
+ import {fromStates} from '@taufik-nurrohman/from';
4
3
  import {hasValue} from '@taufik-nurrohman/has';
5
4
  import {isArray, isSet, isString} from '@taufik-nurrohman/is';
5
+ import {onEvent, offEvent, offEventDefault} from '@taufik-nurrohman/event';
6
6
  import {toCount, toObjectValues} from '@taufik-nurrohman/to';
7
+ import {toPattern} from '@taufik-nurrohman/pattern';
7
8
 
8
- const pairs = {
9
- '`': '`',
10
- '(': ')',
11
- '{': '}',
12
- '[': ']',
13
- '"': '"',
14
- "'": "'",
15
- '<': '>'
16
- };
17
-
18
- function promisify(type, lot) {
19
- return new Promise((resolve, reject) => {
20
- let r = W[type].apply(W, lot);
21
- return r ? resolve(r) : reject(r);
22
- });
23
- }
24
-
25
- const defaults = {
26
- source: {
27
- pairs,
28
- type: null
29
- }
30
- };
31
-
32
- ['alert', 'confirm', 'prompt'].forEach(type => {
33
- defaults.source[type] = (...lot) => promisify(type, lot);
34
- });
9
+ const ALT_PREFIX = 'Alt-';
10
+ const CTRL_PREFIX = 'Control-';
11
+ const SHIFT_PREFIX = 'Shift-';
35
12
 
36
- export const that = {};
13
+ const bounce = debounce($ => $.record(), 10);
37
14
 
38
- that.toggle = function (open, close, wrap, tidy = false) {
39
- if (!close && "" !== close) {
40
- close = open;
15
+ function onKeyDown(e) {
16
+ let self = this,
17
+ editor = self.TextEditor,
18
+ keys = editor.k();
19
+ if (!editor || e.defaultPrevented) {
20
+ return;
41
21
  }
42
- let t = this,
43
- {after, before, value} = t.$(),
44
- closeCount = toCount(close),
45
- openCount = toCount(open);
46
- if (
47
- (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
48
- (close === after.slice(0, closeCount) && open === before.slice(-openCount))
49
- ) {
50
- return t.peel(open, close, wrap);
22
+ bounce(editor);
23
+ if (editor.keys[keys]) {
24
+ return;
51
25
  }
52
- if (false !== tidy) {
53
- if (isString(tidy)) {
54
- tidy = [tidy, tidy];
55
- } else if (!isArray(tidy)) {
56
- tidy = ["", ""];
26
+ let charAfter,
27
+ charBefore,
28
+ charIndent = editor.state.source?.tab || editor.state.tab || '\t',
29
+ charPairs = editor.state.source?.pairs || {},
30
+ charPairsValues = toObjectValues(charPairs);
31
+ let {after, before, end, start, value} = editor.$(),
32
+ lineAfter = after.split('\n').shift(),
33
+ lineBefore = before.split('\n').pop(),
34
+ lineMatch = lineBefore.match(/^(\s+)/),
35
+ lineMatchIndent = lineMatch && lineMatch[1] || "";
36
+ if (CTRL_PREFIX + SHIFT_PREFIX + 'Enter' === keys) {
37
+ if (before || after) {
38
+ // Insert line above with `⎈⇧↵`
39
+ offEventDefault(e);
40
+ return editor.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
57
41
  }
58
- if (!isSet(tidy[1])) {
59
- tidy[1] = tidy[0];
42
+ return;
43
+ }
44
+ if (CTRL_PREFIX + 'Enter' === keys) {
45
+ if (before || after) {
46
+ // Insert line below with `⎈↵`
47
+ offEventDefault(e);
48
+ return editor.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
60
49
  }
61
- t.trim(tidy[0], tidy[1]);
62
50
  }
63
- return t.wrap(open, close, wrap);
64
- };
65
-
66
- const ALT_PREFIX = 'Alt-';
67
- const CTRL_PREFIX = 'Control-';
68
- const SHIFT_PREFIX = 'Shift-';
69
-
70
- export function canKeyDown(map, of) {
71
- let charAfter,
72
- charBefore,
73
- charIndent = of.state.source.tab || of.state.tab || '\t',
74
- charPairs = of.state.source.pairs || {},
75
- charPairsValues = toObjectValues(charPairs),
76
- {key, queue} = map,
77
- keyValue = map + "";
78
51
  // Do nothing
79
- if (queue.Alt || queue.Control) {
80
- return true;
52
+ if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
53
+ offEventDefault(e);
54
+ return;
81
55
  }
82
- if (' ' === keyValue) {
83
- let {after, before, value} = of.$();
56
+ if (' ' === keys) {
84
57
  charAfter = charPairs[charBefore = before.slice(-1)];
85
58
  if (!value && charAfter && charBefore && charAfter === after[0]) {
86
- of.wrap(' ', ' ');
87
- return false;
59
+ offEventDefault(e);
60
+ return editor.wrap(' ', ' ');
88
61
  }
89
- return true;
62
+ return;
90
63
  }
91
- if ('Enter' === keyValue || SHIFT_PREFIX + 'Enter' === keyValue) {
92
- let {after, before, value} = of.$(),
93
- lineBefore = before.split('\n').pop(),
94
- lineMatch = lineBefore.match(/^(\s+)/),
95
- lineMatchIndent = lineMatch && lineMatch[1] || "";
96
- if (!value) {
97
- if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
98
- of.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
99
- return false;
100
- }
101
- if (lineMatchIndent) {
102
- of.insert('\n' + lineMatchIndent, -1).record();
103
- return false;
104
- }
105
- }
106
- return true;
107
- }
108
- if ('Backspace' === keyValue) {
109
- let {after, before, value} = of.$(),
110
- lineAfter = after.split('\n')[0],
111
- lineBefore = before.split('\n').pop(),
112
- lineMatch = lineBefore.match(/^(\s+)/),
113
- lineMatchIndent = lineMatch && lineMatch[1] || "";
64
+ if ('Backspace' === keys) {
114
65
  charAfter = charPairs[charBefore = before.slice(-1)];
115
66
  // Do nothing on escape
116
67
  if ('\\' === charBefore) {
117
- return true;
68
+ return;
118
69
  }
119
70
  if (value) {
120
71
  if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
121
- of.record().peel(charBefore, charAfter).record();
122
- return false;
72
+ offEventDefault(e);
73
+ return editor.record().peel(charBefore, charAfter).record();
123
74
  }
124
- return true;
75
+ return;
125
76
  }
126
77
  charAfter = charPairs[charBefore = before.trim().slice(-1)];
127
78
  if (charAfter && charBefore) {
@@ -130,205 +81,188 @@ export function canKeyDown(map, of) {
130
81
  after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)
131
82
  ) {
132
83
  // Collapse bracket(s)
133
- of.trim("", "").record();
134
- return false;
84
+ offEventDefault(e);
85
+ return editor.trim("", "").record();
135
86
  }
136
87
  }
137
88
  // Outdent
138
89
  if (lineBefore.endsWith(charIndent)) {
139
- of.pull(charIndent).record();
140
- return false;
90
+ offEventDefault(e);
91
+ return editor.pull(charIndent).record();
141
92
  }
142
93
  if (after && before && !before.endsWith('\\' + charBefore)) {
143
94
  if (charAfter === after[0] && charBefore === before.slice(-1)) {
144
95
  // Peel pair
145
- of.peel(charBefore, charAfter).record();
146
- return false;
96
+ offEventDefault(e);
97
+ return editor.peel(charBefore, charAfter).record();
98
+ }
99
+ }
100
+ return;
101
+ }
102
+ if ('Enter' === keys || SHIFT_PREFIX + 'Enter' === keys) {
103
+ if (!value) {
104
+ if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
105
+ offEventDefault(e);
106
+ return editor.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
107
+ }
108
+ if (lineMatchIndent) {
109
+ offEventDefault(e);
110
+ return editor.insert('\n' + lineMatchIndent, -1).record();
147
111
  }
148
112
  }
149
- return true;
113
+ return;
150
114
  }
151
- let {after, before, start, value} = of.$();
152
115
  // Do nothing on escape
153
116
  if ('\\' === (charBefore = before.slice(-1))) {
154
- return true;
117
+ return;
155
118
  }
156
119
  charAfter = hasValue(after[0], charPairsValues) ? after[0] : charPairs[charBefore];
120
+ keys = keys.replace(SHIFT_PREFIX, "");
157
121
  // `|}`
158
- if (!value && after && before && charAfter && key === charAfter) {
122
+ if (!value && after && before && charAfter && keys === charAfter) {
159
123
  // Move to the next character
160
124
  // `}|`
161
- of.select(start + 1).record();
162
- return false;
125
+ offEventDefault(e);
126
+ return editor.select(start + 1).record();
163
127
  }
164
128
  for (charBefore in charPairs) {
165
129
  charAfter = charPairs[charBefore];
166
130
  // `{|`
167
- if (key === charBefore && charAfter) {
131
+ if (keys === charBefore && charAfter) {
168
132
  // Wrap pair or selection
169
133
  // `{|}` `{|aaa|}`
170
- of.wrap(charBefore, charAfter).record();
171
- return false;
134
+ offEventDefault(e);
135
+ return editor.wrap(charBefore, charAfter).record();
172
136
  }
173
137
  // `|}`
174
- if (key === charAfter) {
138
+ if (keys === charAfter) {
175
139
  if (value) {
176
140
  // Wrap selection
177
141
  // `{|aaa|}`
178
- of.record().wrap(charBefore, charAfter).record();
179
- return false;
142
+ offEventDefault(e);
143
+ return editor.record().wrap(charBefore, charAfter).record();
180
144
  }
181
145
  break;
182
146
  }
183
147
  }
184
- return true;
185
- }
186
-
187
- export function canKeyDownDent(map, of) {
188
- let charIndent = of.state.source.tab || of.state.tab || '\t',
189
- {key, queue} = map,
190
- keyValue = map + "";
191
- // Indent with `⎈]`
192
- if (CTRL_PREFIX + ']' === keyValue) {
193
- of.push(charIndent).record();
194
- return false;
195
- }
196
- // Outdent with `⎈[`
197
- if (CTRL_PREFIX + '[' === keyValue) {
198
- of.pull(charIndent).record();
199
- return false;
200
- }
201
- return true;
202
- }
203
-
204
- export function canKeyDownEnter(map, of) {
205
- let {key, queue} = map;
206
- if (queue.Control && queue.Enter) {
207
- let {after, before, end, start, value} = of.$(),
208
- lineAfter = after.split('\n').shift(),
209
- lineBefore = before.split('\n').pop(),
210
- lineMatch = lineBefore.match(/^(\s+)/),
211
- lineMatchIndent = lineMatch && lineMatch[1] || "";
212
- if (before || after) {
213
- if (queue.Shift) {
214
- // Insert line above with `⎈⇧↵`
215
- return of.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
216
- }
217
- // Insert line below with `⎈↵`
218
- return of.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
219
- }
220
- }
221
- return true;
222
- }
223
-
224
- export function canKeyDownHistory(map, of) {
225
- let keyValue = map + "";
226
- // Redo with `⎈y`
227
- if (CTRL_PREFIX + 'y' === keyValue) {
228
- return of.redo(), false;
229
- }
230
- // Undo with `⎈z`
231
- if (CTRL_PREFIX + 'z' === keyValue) {
232
- return of.undo(), false;
233
- }
234
- return true;
235
- }
236
-
237
- export function canKeyDownMove(map, of) {
238
- let {key, queue} = map,
239
- keyValue = map + "";
240
- if (!queue.Control) {
241
- return true;
242
- }
243
- let {after, before, end, start, value} = of.$(),
244
- charPair, charPairValue,
245
- charPairs = of.state.source.pairs || {},
246
- boundaries = [], m;
148
+ let charPair,
149
+ charPairValue, m,
150
+ tokens = [];
247
151
  if (value) {
248
152
  for (charPair in charPairs) {
249
153
  if (!(charPairValue = charPairs[charPair])) {
250
154
  continue;
251
155
  }
252
- boundaries.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
156
+ tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
253
157
  }
254
- boundaries.push('\\w+'); // Word(s)
255
- boundaries.push('\\s+'); // White-space(s)
256
- boundaries.push('[\\s\\S]'); // Last try!
257
- if (CTRL_PREFIX + 'ArrowLeft' === keyValue) {
258
- if (m = before.match(toPattern('(' + boundaries.join('|') + ')$', ""))) {
259
- of.insert("").select(start - toCount(m[0])).insert(value);
260
- return of.record(), false;
158
+ tokens.push('\\w+'); // Word(s)
159
+ tokens.push('\\s+'); // White-space(s)
160
+ tokens.push('[\\s\\S]'); // Last try!
161
+ if (CTRL_PREFIX + 'ArrowLeft' === keys) {
162
+ offEventDefault(e);
163
+ if (m = before.match(toPattern('(' + tokens.join('|') + ')$', ""))) {
164
+ return editor.insert("").select(start - toCount(m[0])).insert(value).record();
261
165
  }
262
- return of.select(), false;
166
+ return editor.select();
263
167
  }
264
- if (CTRL_PREFIX + 'ArrowRight' === keyValue) {
265
- if (m = after.match(toPattern('^(' + boundaries.join('|') + ')', ""))) {
266
- of.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value);
267
- return of.record(), false;
168
+ if (CTRL_PREFIX + 'ArrowRight' === keys) {
169
+ offEventDefault(e);
170
+ if (m = after.match(toPattern('^(' + tokens.join('|') + ')', ""))) {
171
+ return editor.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value).record();
268
172
  }
269
- return of.select(), false;
173
+ return editor.select();
270
174
  }
271
175
  }
272
- let lineAfter = after.split('\n').shift(),
273
- lineBefore = before.split('\n').pop(),
274
- lineMatch = lineBefore.match(/^(\s+)/),
275
- lineMatchIndent = lineMatch && lineMatch[1] || "";
276
176
  // Force to select the current line if there is no selection
277
177
  end += toCount(lineAfter);
278
178
  start -= toCount(lineBefore);
279
179
  value = lineBefore + value + lineAfter;
280
- if (CTRL_PREFIX + 'ArrowUp' === keyValue) {
180
+ if (CTRL_PREFIX + 'ArrowUp' === keys) {
181
+ offEventDefault(e);
281
182
  if (!hasValue('\n', before)) {
282
- return of.select(), false;
183
+ return editor.select();
283
184
  }
284
- of.insert("");
285
- of.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
286
- of.replace(/(^|\n)([^\n]*?)$/, "", -1);
287
- let $ = of.$();
185
+ editor.insert("");
186
+ editor.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
187
+ editor.replace(/(^|\n)([^\n]*?)$/, "", -1);
188
+ let $ = editor.$();
288
189
  before = $.before;
289
190
  start = $.start;
290
191
  lineBefore = before.split('\n').pop();
291
- of.select(start = start - toCount(lineBefore)).wrap(value, '\n');
292
- of.select(start, start + toCount(value));
293
- return of.record(), false;
192
+ editor.select(start = start - toCount(lineBefore)).wrap(value, '\n');
193
+ editor.select(start, start + toCount(value));
194
+ return editor.record();
294
195
  }
295
- if (CTRL_PREFIX + 'ArrowDown' === keyValue) {
196
+ if (CTRL_PREFIX + 'ArrowDown' === keys) {
197
+ offEventDefault(e);
296
198
  if (!hasValue('\n', after)) {
297
- return of.select(), false;
199
+ return editor.select();
298
200
  }
299
- of.insert("");
300
- of.replace(/^([^\n]*?)(\n|$)/, "", 1);
301
- of.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
302
- let $ = of.$();
201
+ editor.insert("");
202
+ editor.replace(/^([^\n]*?)(\n|$)/, "", 1);
203
+ editor.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
204
+ let $ = editor.$();
303
205
  after = $.after;
304
206
  end = $.end;
305
207
  lineAfter = after.split('\n').shift();
306
- of.select(end = end + toCount(lineAfter)).wrap('\n', value);
208
+ editor.select(end = end + toCount(lineAfter)).wrap('\n', value);
307
209
  end += 1;
308
- of.select(end, end + toCount(value));
309
- return of.record(), false;
210
+ editor.select(end, end + toCount(value));
211
+ return editor.record();
310
212
  }
311
- return true;
213
+ return;
312
214
  }
313
215
 
314
- export function canKeyDownTab(map, of) {
315
- let charIndent = of.state.source.tab || of.state.tab || '\t',
316
- keyValue = map + "";
317
- // Indent with `⇥`
318
- if ('Tab' === keyValue) {
319
- return of.push(charIndent).record(), false;
320
- }
321
- // Outdent with `⇧+⇥`
322
- if (SHIFT_PREFIX + 'Tab' === keyValue) {
323
- return of.pull(charIndent).record(), false;
324
- }
325
- return true;
216
+ function attach(self) {
217
+ let $ = this;
218
+ $.state = fromStates({
219
+ source: {
220
+ pairs: {
221
+ '`': '`',
222
+ '(': ')',
223
+ '{': '}',
224
+ '[': ']',
225
+ '"': '"',
226
+ "'": "'",
227
+ '<': '>'
228
+ },
229
+ type: null
230
+ }
231
+ }, $.state);
232
+ $.toggle = function (open, close, wrap, tidy = false) {
233
+ if (!close && "" !== close) {
234
+ close = open;
235
+ }
236
+ let {after, before, value} = $.$(),
237
+ closeCount = toCount(close),
238
+ openCount = toCount(open);
239
+ if (
240
+ (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
241
+ (close === after.slice(0, closeCount) && open === before.slice(-openCount))
242
+ ) {
243
+ return $.peel(open, close, wrap);
244
+ }
245
+ if (false !== tidy) {
246
+ if (isString(tidy)) {
247
+ tidy = [tidy, tidy];
248
+ } else if (!isArray(tidy)) {
249
+ tidy = ["", ""];
250
+ }
251
+ if (!isSet(tidy[1])) {
252
+ tidy[1] = tidy[0];
253
+ }
254
+ $.trim(tidy[0], tidy[1]);
255
+ }
256
+ return $.wrap(open, close, wrap);
257
+ };
258
+ onEvent('keydown', self, onKeyDown);
259
+ return $.record();
326
260
  }
327
261
 
328
- let bounce = debounce(of => of.record(), 100);
329
-
330
- export function canKeyUp(map, of) {
331
- return bounce(of), true;
262
+ function detach(self) {
263
+ let $ = this;
264
+ offEvent('keydown', self, onKeyDown);
265
+ return $;
332
266
  }
333
267
 
334
- export const state = defaults;
268
+ export default {attach, detach};
package/package.json CHANGED
@@ -3,11 +3,14 @@
3
3
  "browser": "index.min.js",
4
4
  "bugs": "https://github.com/taufik-nurrohman/text-editor.source/issues",
5
5
  "dependencies": {
6
+ "@taufik-nurrohman/event": "*",
7
+ "@taufik-nurrohman/from": "*",
6
8
  "@taufik-nurrohman/has": "*",
7
- "@taufik-nurrohman/key": "*",
9
+ "@taufik-nurrohman/is": "*",
8
10
  "@taufik-nurrohman/pattern": "*",
9
11
  "@taufik-nurrohman/text-editor": "*",
10
12
  "@taufik-nurrohman/text-editor.history": "*",
13
+ "@taufik-nurrohman/text-editor.key": "*",
11
14
  "@taufik-nurrohman/tick": "*",
12
15
  "@taufik-nurrohman/to": "*"
13
16
  },
@@ -47,7 +50,7 @@
47
50
  "url": "git+https://github.com/taufik-nurrohman/text-editor.source.git"
48
51
  },
49
52
  "scripts": {
50
- "pack": "pack --clean=false --from=.github/factory --js-format=umd --js-name=TE.Source --js-top='%(js.license)' --mjs=true --to=."
53
+ "pack": "pack --clean=false --from=.factory --js-format=umd --js-name=TextEditor.Source --js-top='%(js.license)' --mjs=true --to=."
51
54
  },
52
- "version": "2.2.11"
55
+ "version": "3.0.0"
53
56
  }