@textbus/platform-browser 3.0.0-alpha.24 → 3.0.0-alpha.26

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/bundles/index.js CHANGED
@@ -37,6 +37,67 @@ function createElement(tagName, options = {}) {
37
37
  }
38
38
  function createTextNode(content) {
39
39
  return document.createTextNode(content);
40
+ }
41
+ function getLayoutRectByRange(range) {
42
+ let { startContainer, startOffset } = range;
43
+ if (startContainer.nodeType === Node.TEXT_NODE) {
44
+ if (startOffset > 0) {
45
+ return range.getBoundingClientRect();
46
+ }
47
+ const parentNode = startContainer.parentNode;
48
+ startOffset = Array.from(parentNode.childNodes).indexOf(startContainer);
49
+ startContainer = parentNode;
50
+ }
51
+ const beforeNode = startContainer.childNodes[startOffset - 1];
52
+ if (beforeNode) {
53
+ if (beforeNode.nodeType === Node.ELEMENT_NODE && beforeNode.nodeName.toLowerCase() !== 'br') {
54
+ const rect = beforeNode.getBoundingClientRect();
55
+ return {
56
+ left: rect.right,
57
+ top: rect.top,
58
+ width: rect.width,
59
+ height: rect.height
60
+ };
61
+ }
62
+ else if (beforeNode.nodeType === Node.TEXT_NODE) {
63
+ const range2 = document.createRange();
64
+ range2.setStart(beforeNode, beforeNode.textContent.length);
65
+ range2.setEnd(beforeNode, beforeNode.textContent.length);
66
+ return range2.getBoundingClientRect();
67
+ }
68
+ }
69
+ const offsetNode = startContainer.childNodes[startOffset];
70
+ let isInsertBefore = false;
71
+ if (!offsetNode) {
72
+ const lastChild = startContainer.lastChild;
73
+ if (lastChild && lastChild.nodeType === Node.ELEMENT_NODE) {
74
+ const rect = lastChild.getBoundingClientRect();
75
+ return {
76
+ left: rect.right,
77
+ top: rect.top,
78
+ width: rect.width,
79
+ height: rect.height
80
+ };
81
+ }
82
+ }
83
+ if (offsetNode) {
84
+ if (offsetNode.nodeType === Node.ELEMENT_NODE && offsetNode.nodeName.toLowerCase() !== 'br') {
85
+ return offsetNode.getBoundingClientRect();
86
+ }
87
+ isInsertBefore = true;
88
+ }
89
+ const span = startContainer.ownerDocument.createElement('span');
90
+ span.innerText = '\u200b';
91
+ span.style.display = 'inline-block';
92
+ if (isInsertBefore) {
93
+ startContainer.insertBefore(span, offsetNode);
94
+ }
95
+ else {
96
+ startContainer.appendChild(span);
97
+ }
98
+ const rect = span.getBoundingClientRect();
99
+ startContainer.removeChild(span);
100
+ return rect;
40
101
  }
41
102
 
42
103
  const isWindows = () => /win(dows|32|64)/i.test(navigator.userAgent);
@@ -100,1444 +161,758 @@ const VIEW_DOCUMENT = new di.InjectionToken('VIEW_DOCUMENT');
100
161
  */
101
162
  const VIEW_MASK = new di.InjectionToken('VIEW_MASK');
102
163
 
103
- function getLayoutRectByRange(range) {
104
- let { startContainer, startOffset } = range;
105
- if (startContainer.nodeType === Node.TEXT_NODE) {
106
- if (startOffset > 0) {
107
- return range.getBoundingClientRect();
164
+ class Input {
165
+ }
166
+
167
+ /**
168
+ * Textbus PC 端选区桥接实现
169
+ */
170
+ exports.SelectionBridge = class SelectionBridge {
171
+ constructor(config, injector, controller, selection, rootComponentRef, input, renderer) {
172
+ this.config = config;
173
+ this.injector = injector;
174
+ this.controller = controller;
175
+ this.selection = selection;
176
+ this.rootComponentRef = rootComponentRef;
177
+ this.input = input;
178
+ this.renderer = renderer;
179
+ this.nativeSelection = document.getSelection();
180
+ this.selectionMaskElement = createElement('style');
181
+ this.selectionChangeEvent = new stream.Subject();
182
+ this.subs = [];
183
+ this.connector = null;
184
+ this.ignoreSelectionChange = false;
185
+ this.changeFromUser = false;
186
+ this.docContainer = injector.get(VIEW_DOCUMENT);
187
+ this.maskContainer = injector.get(VIEW_MASK);
188
+ this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(stream.filter(() => {
189
+ return !controller.readonly;
190
+ }));
191
+ document.head.appendChild(this.selectionMaskElement);
192
+ this.sub = this.onSelectionChange.subscribe((r) => {
193
+ if (r) {
194
+ input.focus(r, this.changeFromUser);
195
+ }
196
+ else {
197
+ input.blur();
198
+ }
199
+ });
200
+ this.sub.add(stream.fromEvent(document, 'focusin').subscribe(ev => {
201
+ let target = ev.target;
202
+ if (/^(input|textarea|select)$/i.test(target.nodeName)) {
203
+ if (target.tagName.toLowerCase() === 'input' && /^(range|date)$/.test(target.type)) {
204
+ return;
205
+ }
206
+ this.ignoreSelectionChange = true;
207
+ return;
208
+ }
209
+ if (!config.useContentEditable) {
210
+ while (target) {
211
+ if (target.contentEditable === 'true') {
212
+ this.ignoreSelectionChange = true;
213
+ return;
214
+ }
215
+ target = target.parentNode;
216
+ }
217
+ }
218
+ }));
219
+ this.sub.add(stream.fromEvent(document, 'focusout').subscribe(() => {
220
+ this.ignoreSelectionChange = false;
221
+ }));
222
+ }
223
+ connect(connector) {
224
+ this.disConnect();
225
+ this.connector = connector;
226
+ this.syncSelection(connector);
227
+ this.listen(connector);
228
+ }
229
+ disConnect() {
230
+ this.connector = null;
231
+ this.unListen();
232
+ }
233
+ getRect(location) {
234
+ const { focus, anchor } = this.getPositionByRange({
235
+ focusOffset: location.offset,
236
+ anchorOffset: location.offset,
237
+ focusSlot: location.slot,
238
+ anchorSlot: location.slot
239
+ });
240
+ if (!focus || !anchor) {
241
+ return null;
108
242
  }
109
- const parentNode = startContainer.parentNode;
110
- startOffset = Array.from(parentNode.childNodes).indexOf(startContainer);
111
- startContainer = parentNode;
243
+ const nativeRange = document.createRange();
244
+ nativeRange.setStart(focus.node, focus.offset);
245
+ nativeRange.collapse();
246
+ return getLayoutRectByRange(nativeRange);
112
247
  }
113
- const beforeNode = startContainer.childNodes[startOffset - 1];
114
- if (beforeNode) {
115
- if (beforeNode.nodeType === Node.ELEMENT_NODE && beforeNode.nodeName.toLowerCase() !== 'br') {
116
- const rect = beforeNode.getBoundingClientRect();
117
- return {
118
- left: rect.right,
119
- top: rect.top,
120
- width: rect.width,
121
- height: rect.height
122
- };
248
+ restore(abstractSelection, formLocal) {
249
+ this.changeFromUser = formLocal;
250
+ if (this.ignoreSelectionChange || !this.connector) {
251
+ return;
123
252
  }
124
- else if (beforeNode.nodeType === Node.TEXT_NODE) {
125
- const range2 = document.createRange();
126
- range2.setStart(beforeNode, beforeNode.textContent.length);
127
- range2.setEnd(beforeNode, beforeNode.textContent.length);
128
- return range2.getBoundingClientRect();
253
+ this.unListen();
254
+ if (!abstractSelection) {
255
+ this.nativeSelection.removeAllRanges();
256
+ this.selectionChangeEvent.next(null);
257
+ this.listen(this.connector);
258
+ return;
259
+ }
260
+ const { focus, anchor } = this.getPositionByRange(abstractSelection);
261
+ if (!focus || !anchor) {
262
+ this.nativeSelection.removeAllRanges();
263
+ this.selectionChangeEvent.next(null);
264
+ this.listen(this.connector);
265
+ return;
266
+ }
267
+ this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
268
+ if (this.nativeSelection.rangeCount) {
269
+ const nativeRange = this.nativeSelection.getRangeAt(0);
270
+ this.selectionChangeEvent.next(nativeRange);
271
+ }
272
+ else {
273
+ this.selectionChangeEvent.next(null);
274
+ }
275
+ // hack start 浏览器会触发上面选区更改事件
276
+ const bind = () => {
277
+ if (this.connector) {
278
+ this.listen(this.connector);
279
+ }
280
+ };
281
+ if (typeof requestIdleCallback === 'function') {
282
+ requestIdleCallback(bind);
283
+ }
284
+ else {
285
+ setTimeout(bind, 30);
129
286
  }
287
+ // hack end
130
288
  }
131
- const offsetNode = startContainer.childNodes[startOffset];
132
- let isInsertBefore = false;
133
- if (!offsetNode) {
134
- const lastChild = startContainer.lastChild;
135
- if (lastChild && lastChild.nodeType === Node.ELEMENT_NODE) {
136
- const rect = lastChild.getBoundingClientRect();
289
+ destroy() {
290
+ this.sub.unsubscribe();
291
+ }
292
+ getPositionByRange(abstractSelection) {
293
+ let focus;
294
+ let anchor;
295
+ try {
296
+ focus = this.findSelectedNodeAndOffset(abstractSelection.focusSlot, abstractSelection.focusOffset);
297
+ anchor = focus;
298
+ if (abstractSelection.anchorSlot !== abstractSelection.focusSlot ||
299
+ abstractSelection.anchorOffset !== abstractSelection.focusOffset) {
300
+ anchor = this.findSelectedNodeAndOffset(abstractSelection.anchorSlot, abstractSelection.anchorOffset);
301
+ }
137
302
  return {
138
- left: rect.right,
139
- top: rect.top,
140
- width: rect.width,
141
- height: rect.height
303
+ focus,
304
+ anchor
142
305
  };
143
306
  }
144
- }
145
- if (offsetNode) {
146
- if (offsetNode.nodeType === Node.ELEMENT_NODE && offsetNode.nodeName.toLowerCase() !== 'br') {
147
- return offsetNode.getBoundingClientRect();
307
+ catch (e) {
308
+ return {
309
+ focus: null,
310
+ anchor: null
311
+ };
148
312
  }
149
- isInsertBefore = true;
150
313
  }
151
- const span = startContainer.ownerDocument.createElement('span');
152
- span.innerText = '\u200b';
153
- span.style.display = 'inline-block';
154
- if (isInsertBefore) {
155
- startContainer.insertBefore(span, offsetNode);
314
+ getPreviousLinePositionByCurrent(position) {
315
+ return this.getLinePosition(position, false);
156
316
  }
157
- else {
158
- startContainer.appendChild(span);
317
+ getNextLinePositionByCurrent(position) {
318
+ return this.getLinePosition(position, true);
159
319
  }
160
- const rect = span.getBoundingClientRect();
161
- startContainer.removeChild(span);
162
- return rect;
163
- }
164
- exports.Caret = class Caret {
165
- constructor(scheduler, injector) {
166
- this.scheduler = scheduler;
167
- this.injector = injector;
168
- this.timer = null;
169
- this.oldPosition = null;
170
- this._display = true;
171
- this.flashing = true;
172
- this.subs = [];
173
- this.positionChangeEvent = new stream.Subject();
174
- this.styleChangeEvent = new stream.Subject();
175
- this.oldRange = null;
176
- this.isFixed = false;
177
- this.editorMask = injector.get(VIEW_MASK);
178
- this.onPositionChange = this.positionChangeEvent.pipe(stream.distinctUntilChanged());
179
- this.onStyleChange = this.styleChangeEvent.asObservable();
180
- this.elementRef = createElement('div', {
181
- styles: {
182
- position: 'absolute',
183
- width: '2px',
184
- pointerEvents: 'none'
185
- },
186
- children: [
187
- this.caret = createElement('span', {
188
- styles: {
189
- width: '100%',
190
- height: '100%',
191
- position: 'absolute',
192
- left: 0,
193
- top: 0
194
- }
195
- })
196
- ]
197
- });
198
- this.subs.push(stream.fromEvent(document, 'mousedown').subscribe(() => {
199
- this.flashing = false;
200
- }), stream.fromEvent(document, 'mouseup').subscribe(() => {
201
- this.flashing = true;
202
- }));
203
- this.editorMask.appendChild(this.elementRef);
204
- }
205
- set display(v) {
206
- this._display = v;
207
- this.caret.style.visibility = v ? 'visible' : 'hidden';
208
- }
209
- get display() {
210
- return this._display;
211
- }
212
- refresh(isFixedCaret = false) {
213
- this.isFixed = isFixedCaret;
214
- if (this.oldRange) {
215
- this.show(this.oldRange, false);
216
- }
217
- this.isFixed = false;
218
- }
219
- show(range, restart) {
220
- const oldRect = this.elementRef.getBoundingClientRect();
221
- this.oldPosition = {
222
- top: oldRect.top,
223
- left: oldRect.left,
224
- height: oldRect.height
225
- };
226
- this.oldRange = range;
227
- if (restart || this.scheduler.lastChangesHasLocalUpdate) {
228
- clearTimeout(this.timer);
229
- }
230
- this.updateCursorPosition(range);
231
- if (range.collapsed) {
232
- if (restart || this.scheduler.lastChangesHasLocalUpdate) {
233
- this.display = true;
234
- const toggleShowHide = () => {
235
- this.display = !this.display || !this.flashing;
236
- this.timer = setTimeout(toggleShowHide, 400);
237
- };
238
- clearTimeout(this.timer);
239
- this.timer = setTimeout(toggleShowHide, 400);
240
- }
320
+ getLinePosition(currentPosition, toNext) {
321
+ clearTimeout(this.cacheCaretPositionTimer);
322
+ let p;
323
+ if (this.oldCaretPosition) {
324
+ p = toNext ?
325
+ this.getNextLinePositionByOffset(currentPosition, this.oldCaretPosition.left) :
326
+ this.getPreviousLinePositionByOffset(currentPosition, this.oldCaretPosition.left);
241
327
  }
242
328
  else {
243
- this.display = false;
244
- clearTimeout(this.timer);
329
+ this.oldCaretPosition = this.getRect(currentPosition);
330
+ p = toNext ?
331
+ this.getNextLinePositionByOffset(currentPosition, this.oldCaretPosition.left) :
332
+ this.getPreviousLinePositionByOffset(currentPosition, this.oldCaretPosition.left);
245
333
  }
334
+ this.cacheCaretPositionTimer = setTimeout(() => {
335
+ this.oldCaretPosition = null;
336
+ }, 3000);
337
+ return p;
246
338
  }
247
- hide() {
248
- this.display = false;
249
- clearTimeout(this.timer);
250
- this.positionChangeEvent.next(null);
251
- }
252
- destroy() {
253
- clearTimeout(this.timer);
254
- this.subs.forEach(i => i.unsubscribe());
255
- }
256
- correctScrollTop(scroller) {
257
- const scheduler = this.scheduler;
258
- let docIsChanged = true;
259
- function limitPosition(position) {
260
- const { top, bottom } = scroller.getLimit();
261
- const caretTop = position.top;
262
- if (caretTop + position.height > bottom) {
263
- const offset = caretTop - bottom + position.height;
264
- scroller.setOffset(offset);
339
+ /**
340
+ * 获取选区向上移动一行的位置。
341
+ * @param currentPosition
342
+ * @param startLeft 参考位置。
343
+ */
344
+ getPreviousLinePositionByOffset(currentPosition, startLeft) {
345
+ let isToPrevLine = false;
346
+ let loopCount = 0;
347
+ let minLeft = startLeft;
348
+ let focusSlot = currentPosition.slot;
349
+ let focusOffset = currentPosition.offset;
350
+ let minTop = this.getRect({
351
+ slot: focusSlot,
352
+ offset: focusOffset
353
+ }).top;
354
+ let position;
355
+ let oldPosition;
356
+ let oldLeft = 0;
357
+ while (true) {
358
+ loopCount++;
359
+ position = this.selection.getPreviousPositionByPosition(focusSlot, focusOffset);
360
+ focusSlot = position.slot;
361
+ focusOffset = position.offset;
362
+ const rect2 = this.getRect(position);
363
+ if (!isToPrevLine) {
364
+ if (rect2.left > minLeft || rect2.top < minTop) {
365
+ isToPrevLine = true;
366
+ }
367
+ else if (rect2.left === minLeft && rect2.top === minTop) {
368
+ return position;
369
+ }
370
+ minLeft = rect2.left;
371
+ minTop = rect2.top;
265
372
  }
266
- else if (position.top < top) {
267
- scroller.setOffset(-(top - position.top));
373
+ if (isToPrevLine) {
374
+ if (rect2.left < startLeft) {
375
+ return position;
376
+ }
377
+ if (oldPosition) {
378
+ if (rect2.left >= oldLeft) {
379
+ return oldPosition;
380
+ }
381
+ }
382
+ oldLeft = rect2.left;
383
+ oldPosition = position;
384
+ }
385
+ if (loopCount > 10000) {
386
+ break;
268
387
  }
269
388
  }
270
- let isPressed = false;
271
- this.subs.push(scroller.onScroll.subscribe(() => {
272
- if (this.oldPosition) {
273
- const rect = this.elementRef.getBoundingClientRect();
274
- this.oldPosition.top = rect.top;
275
- this.oldPosition.left = rect.left;
276
- this.oldPosition.height = rect.height;
389
+ return position || {
390
+ offset: 0,
391
+ slot: focusSlot
392
+ };
393
+ }
394
+ /**
395
+ * 获取选区向下移动一行的位置。
396
+ * @param currentPosition
397
+ * @param startLeft 参考位置。
398
+ */
399
+ getNextLinePositionByOffset(currentPosition, startLeft) {
400
+ let isToNextLine = false;
401
+ let loopCount = 0;
402
+ let maxRight = startLeft;
403
+ let focusSlot = currentPosition.slot;
404
+ let focusOffset = currentPosition.offset;
405
+ let minTop = this.getRect({
406
+ slot: focusSlot,
407
+ offset: focusOffset
408
+ }).top;
409
+ let oldPosition;
410
+ let oldLeft = 0;
411
+ while (true) {
412
+ loopCount++;
413
+ const position = this.selection.getNextPositionByPosition(focusSlot, focusOffset);
414
+ focusSlot = position.slot;
415
+ focusOffset = position.offset;
416
+ const rect2 = this.getRect(position);
417
+ if (!isToNextLine) {
418
+ if (rect2.left < maxRight || rect2.top > minTop) {
419
+ isToNextLine = true;
420
+ }
421
+ else if (rect2.left === maxRight && rect2.top === minTop) {
422
+ return position;
423
+ }
424
+ maxRight = rect2.left;
425
+ minTop = rect2.top;
426
+ oldPosition = position;
277
427
  }
278
- }), stream.fromEvent(document, 'mousedown', true).subscribe(() => {
279
- isPressed = true;
280
- }), stream.fromEvent(document, 'mouseup', true).subscribe(() => {
281
- isPressed = false;
282
- }), scheduler.onDocChange.subscribe(() => {
283
- docIsChanged = true;
284
- }), this.onPositionChange.subscribe(position => {
285
- if (position) {
286
- if (docIsChanged) {
287
- if (scheduler.lastChangesHasLocalUpdate) {
288
- limitPosition(position);
289
- }
290
- else if (this.oldPosition) {
291
- const offset = Math.floor(position.top - this.oldPosition.top);
292
- scroller.setOffset(offset);
293
- }
428
+ if (isToNextLine) {
429
+ if (rect2.left > startLeft) {
430
+ return oldPosition;
294
431
  }
295
- else if (!isPressed) {
296
- if (this.isFixed && this.oldPosition) {
297
- const offset = Math.floor(position.top - this.oldPosition.top);
298
- scroller.setOffset(offset);
299
- }
300
- else {
301
- limitPosition(position);
432
+ if (oldPosition) {
433
+ if (rect2.left <= oldLeft) {
434
+ return oldPosition;
302
435
  }
303
436
  }
437
+ oldPosition = position;
438
+ oldLeft = rect2.left;
304
439
  }
305
- docIsChanged = false;
440
+ if (loopCount > 10000) {
441
+ break;
442
+ }
443
+ }
444
+ return oldPosition || {
445
+ offset: focusSlot.length,
446
+ slot: focusSlot
447
+ };
448
+ }
449
+ unListen() {
450
+ this.subs.forEach(i => i.unsubscribe());
451
+ this.subs = [];
452
+ }
453
+ listen(connector) {
454
+ if (!this.config.useContentEditable) {
455
+ const selection = this.nativeSelection;
456
+ this.subs.push(stream.fromEvent(this.docContainer, 'mousedown').subscribe(ev => {
457
+ if (this.ignoreSelectionChange || ev.button === 2) {
458
+ return;
459
+ }
460
+ if (!ev.shiftKey) {
461
+ selection.removeAllRanges();
462
+ }
463
+ }));
464
+ }
465
+ this.subs.push(stream.fromEvent(document, 'selectionchange').subscribe(() => {
466
+ this.syncSelection(connector);
306
467
  }));
307
468
  }
308
- updateCursorPosition(nativeRange) {
309
- const startContainer = nativeRange.startContainer;
310
- const node = (startContainer.nodeType === Node.ELEMENT_NODE ? startContainer : startContainer.parentNode);
311
- if ((node === null || node === void 0 ? void 0 : node.nodeType) !== Node.ELEMENT_NODE || !nativeRange.collapsed) {
312
- this.positionChangeEvent.next(null);
469
+ syncSelection(connector) {
470
+ var _a;
471
+ const selection = this.nativeSelection;
472
+ this.changeFromUser = true;
473
+ if (this.ignoreSelectionChange ||
474
+ this.input.composition ||
475
+ selection.rangeCount === 0 ||
476
+ !this.docContainer.contains(selection.anchorNode)) {
313
477
  return;
314
478
  }
315
- const rect = getLayoutRectByRange(nativeRange);
316
- const { fontSize, lineHeight, color } = getComputedStyle(node);
317
- let height;
318
- if (isNaN(+lineHeight)) {
319
- const f = parseFloat(lineHeight);
320
- if (isNaN(f)) {
321
- height = parseFloat(fontSize);
322
- }
479
+ const rawRange = selection.getRangeAt(0);
480
+ const nativeRange = rawRange.cloneRange();
481
+ const isFocusEnd = selection.focusNode === nativeRange.endContainer && selection.focusOffset === nativeRange.endOffset;
482
+ const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
483
+ if (!this.docContainer.contains(selection.focusNode)) {
484
+ if (isFocusEnd) {
485
+ const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.first);
486
+ const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
487
+ nativeRange.setEndAfter(nativeNode.lastChild);
488
+ }
323
489
  else {
324
- height = f;
490
+ const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.last);
491
+ const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
492
+ nativeRange.setStartBefore(nativeNode.firstChild);
325
493
  }
326
494
  }
327
- else {
328
- height = parseFloat(fontSize) * parseFloat(lineHeight);
329
- }
330
- const boxHeight = Math.floor(Math.max(height, rect.height));
331
- // const boxHeight = Math.floor(height)
332
- let rectTop = rect.top;
333
- if (rect.height < height) {
334
- rectTop -= (height - rect.height) / 2;
495
+ const startPosition = this.getCorrectedPosition(nativeRange.startContainer, nativeRange.startOffset, isFocusStart);
496
+ const endPosition = nativeRange.collapsed ?
497
+ startPosition :
498
+ this.getCorrectedPosition(nativeRange.endContainer, nativeRange.endOffset, isFocusEnd);
499
+ if ([Node.ELEMENT_NODE, Node.TEXT_NODE].includes((_a = nativeRange.commonAncestorContainer) === null || _a === void 0 ? void 0 : _a.nodeType) &&
500
+ startPosition && endPosition) {
501
+ const abstractSelection = isFocusEnd ? {
502
+ anchorSlot: startPosition.slot,
503
+ anchorOffset: startPosition.offset,
504
+ focusSlot: endPosition.slot,
505
+ focusOffset: endPosition.offset
506
+ } : {
507
+ focusSlot: startPosition.slot,
508
+ focusOffset: startPosition.offset,
509
+ anchorSlot: endPosition.slot,
510
+ anchorOffset: endPosition.offset
511
+ };
512
+ const { focus, anchor } = this.getPositionByRange(abstractSelection);
513
+ if (focus && anchor) {
514
+ let start = anchor;
515
+ let end = focus;
516
+ if (isFocusStart) {
517
+ start = focus;
518
+ end = anchor;
519
+ }
520
+ if (nativeRange.startContainer !== start.node || nativeRange.startOffset !== start.offset) {
521
+ nativeRange.setStart(start.node, start.offset);
522
+ }
523
+ if (nativeRange.endContainer !== end.node || nativeRange.endOffset !== end.offset) {
524
+ nativeRange.setEnd(end.node, end.offset);
525
+ }
526
+ connector.setSelection(abstractSelection);
527
+ if (selection.isCollapsed) {
528
+ rawRange.setStart(start.node, start.offset);
529
+ rawRange.setEnd(end.node, end.offset);
530
+ }
531
+ this.selectionChangeEvent.next(nativeRange);
532
+ }
533
+ else {
534
+ connector.setSelection(null);
535
+ }
536
+ return;
335
537
  }
336
- rectTop = Math.floor(rectTop);
337
- const containerRect = this.editorMask.getBoundingClientRect();
338
- const top = Math.floor(rectTop - containerRect.top);
339
- const left = Math.floor(rect.left - containerRect.left);
340
- Object.assign(this.elementRef.style, {
341
- left: left + 'px',
342
- top: top + 'px',
343
- height: boxHeight + 'px',
344
- lineHeight: boxHeight + 'px',
345
- fontSize
346
- });
347
- this.caret.style.backgroundColor = color;
348
- this.styleChangeEvent.next({
349
- height: boxHeight + 'px',
350
- lineHeight: boxHeight + 'px',
351
- fontSize
352
- });
353
- this.positionChangeEvent.next({
354
- left,
355
- top: rectTop,
356
- height: boxHeight
357
- });
358
- }
359
- };
360
- exports.Caret = __decorate([
361
- di.Injectable(),
362
- __metadata("design:paramtypes", [core.Scheduler,
363
- di.Injector])
364
- ], exports.Caret);
365
-
366
- var Parser_1;
367
- exports.Parser = Parser_1 = class Parser {
368
- constructor(options, injector) {
369
- var _a;
370
- this.options = options;
371
- this.injector = injector;
372
- const componentLoaders = [
373
- ...(options.componentLoaders || [])
374
- ];
375
- const formatLoaders = [
376
- ...(options.formatLoaders || [])
377
- ];
378
- const attributeLoaders = [
379
- ...(options.attributeLoaders || [])
380
- ];
381
- (_a = options.imports) === null || _a === void 0 ? void 0 : _a.forEach(i => {
382
- componentLoaders.push(...(i.componentLoaders || []));
383
- formatLoaders.push(...(i.formatLoaders || []));
384
- });
385
- this.componentLoaders = componentLoaders;
386
- this.formatLoaders = formatLoaders;
387
- this.attributeLoaders = attributeLoaders;
388
- }
389
- static parseHTML(html) {
390
- return new DOMParser().parseFromString(html, 'text/html').body;
391
- }
392
- parseDoc(html, rootComponentLoader) {
393
- const element = Parser_1.parseHTML(html);
394
- return rootComponentLoader.read(element, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
395
- return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
396
- });
397
- }
398
- parse(html, rootSlot) {
399
- const element = Parser_1.parseHTML(html);
400
- const formatItems = this.readFormats(element, rootSlot, []);
401
- this.applyFormats(rootSlot, formatItems);
402
- return rootSlot;
538
+ connector.setSelection(null);
403
539
  }
404
- readComponent(el, slot, formatItems) {
405
- if (el.nodeType === Node.ELEMENT_NODE) {
406
- if (el.tagName === 'BR') {
407
- slot.insert('\n');
408
- return;
540
+ findSelectedNodeAndOffset(slot, offset) {
541
+ const prev = slot.getContentAtIndex(offset - 1);
542
+ const vNodes = this.renderer.getVNodesBySlot(slot);
543
+ if (prev) {
544
+ if (typeof prev !== 'string') {
545
+ const vNode = this.renderer.getVNodeByComponent(prev);
546
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
547
+ return {
548
+ node: nativeNode.parentNode,
549
+ offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode) + 1
550
+ };
409
551
  }
410
- for (const t of this.componentLoaders) {
411
- if (t.match(el)) {
412
- const result = t.read(el, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
413
- return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
414
- });
415
- if (result instanceof core.Slot) {
416
- result.toDelta().forEach(i => slot.insert(i.insert, i.formats));
417
- return;
552
+ else if (prev === '\n') {
553
+ for (const vNode of vNodes) {
554
+ if (vNode instanceof core.VTextNode) {
555
+ continue;
556
+ }
557
+ if (vNode.tagName === 'br') {
558
+ const position = this.renderer.getLocationByVNode(vNode);
559
+ if (position) {
560
+ if (position.endIndex === offset) {
561
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
562
+ const parentNode = nativeNode.parentNode;
563
+ return {
564
+ node: parentNode,
565
+ offset: Array.from(parentNode.childNodes).indexOf(nativeNode) + 1
566
+ };
567
+ }
568
+ }
418
569
  }
419
- slot.insert(result);
420
- return;
421
570
  }
422
571
  }
423
- this.readFormats(el, slot, formatItems);
424
- }
425
- else if (el.nodeType === Node.TEXT_NODE) {
426
- const textContent = el.textContent;
427
- if (/^\s*[\r\n]+\s*$/.test(textContent)) {
428
- return;
429
- }
430
- slot.insert(textContent);
431
572
  }
432
- }
433
- readFormats(el, slot, formatItems) {
434
- const formats = this.formatLoaders.filter(f => {
435
- return f.match(el);
436
- }).map(f => {
437
- return f.read(el);
438
- });
439
- const startIndex = slot.index;
440
- Array.from(el.childNodes).forEach(child => {
441
- this.readComponent(child, slot, formatItems);
442
- });
443
- const endIndex = slot.index;
444
- formatItems.unshift(...formats.map(i => {
573
+ const current = slot.getContentAtIndex(offset);
574
+ if (current && typeof current !== 'string') {
575
+ const vNode = this.renderer.getVNodeByComponent(current);
576
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
445
577
  return {
446
- formatter: i.formatter,
447
- value: i.value,
448
- startIndex,
449
- endIndex
578
+ node: nativeNode.parentNode,
579
+ offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
450
580
  };
451
- }));
452
- return formatItems;
453
- }
454
- readSlot(childSlot, slotRootElement, slotContentElement) {
455
- this.attributeLoaders.filter(a => {
456
- return a.match(slotRootElement);
457
- }).forEach(a => {
458
- const r = a.read(slotRootElement);
459
- childSlot.setAttribute(r.attribute, r.value);
460
- });
461
- const childFormatItems = this.readFormats(slotContentElement, childSlot, []);
462
- this.applyFormats(childSlot, childFormatItems);
463
- return childSlot;
464
- }
465
- applyFormats(slot, formatItems) {
466
- formatItems.forEach(i => {
467
- slot.retain(i.startIndex);
468
- slot.retain(i.endIndex - i.startIndex, i.formatter, i.value);
469
- });
581
+ }
582
+ for (const vNode of vNodes) {
583
+ if (vNode instanceof core.VElement) {
584
+ if (vNode.tagName === 'br') {
585
+ const position = this.renderer.getLocationByVNode(vNode);
586
+ if (position) {
587
+ if (position.startIndex === offset) {
588
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
589
+ const parentNode = nativeNode.parentNode;
590
+ return {
591
+ node: parentNode,
592
+ offset: Array.from(parentNode.childNodes).indexOf(nativeNode)
593
+ };
594
+ }
595
+ }
596
+ }
597
+ continue;
598
+ }
599
+ const position = this.renderer.getLocationByVNode(vNode);
600
+ if (position) {
601
+ if (offset >= position.startIndex && offset <= position.endIndex) {
602
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
603
+ return {
604
+ node: nativeNode,
605
+ offset: offset - position.startIndex
606
+ };
607
+ }
608
+ }
609
+ }
610
+ return null;
470
611
  }
471
- };
472
- exports.Parser = Parser_1 = __decorate([
473
- di.Injectable(),
474
- __param(0, di.Inject(EDITOR_OPTIONS)),
475
- __metadata("design:paramtypes", [Object, di.Injector])
476
- ], exports.Parser);
477
-
478
- var Input_1;
479
- const iframeHTML = `
480
- <!DOCTYPE html>
481
- <html>
482
- <head>
483
- <meta charset="UTF-8">
484
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
485
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
486
- <title>Textbus</title>
487
- <style>
488
- html {position: fixed; left:0; overflow: hidden}
489
- html, body{height: 100%;width:100%}
490
- body{margin:0; overflow: hidden}
491
- textarea{width: 2000px;height: 100%;opacity: 0; padding: 0; outline: none; border: none; position: absolute; left:0; top:0;}
492
- </style>
493
- </head>
494
- <body>
495
- </body>
496
- </html>
497
- `;
498
- /**
499
- * Textbus PC 端输入实现
500
- */
501
- exports.Input = Input_1 = class Input {
502
- constructor(parser, keyboard, commander, selection, controller, scheduler, caret) {
503
- this.parser = parser;
504
- this.keyboard = keyboard;
505
- this.commander = commander;
506
- this.selection = selection;
507
- this.controller = controller;
508
- this.scheduler = scheduler;
509
- this.caret = caret;
510
- this.container = this.createEditableFrame();
511
- this.subscription = new stream.Subscription();
512
- this.textarea = null;
513
- this.isFocus = false;
514
- this.inputFormatterId = '__TextbusInputFormatter__';
515
- this.inputFormatter = {
516
- name: this.inputFormatterId,
517
- columned: false,
518
- render: (children) => {
519
- return core.jsx('span', {
520
- 'data-writing-format': this.inputFormatterId,
521
- style: {
522
- textDecoration: 'underline'
523
- },
524
- children
525
- });
526
- }
527
- };
528
- this.nativeFocus = false;
529
- this.isSafari = isSafari();
530
- this.isMac = isMac();
531
- this.isWindows = isWindows();
532
- this.isSougouPinYin = false; // 有 bug 版本搜狗拼音
533
- this.onReady = new Promise(resolve => {
534
- this.subscription.add(stream.fromEvent(this.container, 'load').subscribe(() => {
535
- const doc = this.container.contentDocument;
536
- doc.open();
537
- doc.write(iframeHTML);
538
- doc.close();
539
- this.doc = doc;
540
- this.init();
541
- resolve();
542
- }), controller.onReadonlyStateChange.subscribe(() => {
543
- if (controller.readonly) {
544
- this.blur();
545
- }
546
- }));
547
- });
548
- caret.elementRef.append(this.container);
549
- }
550
- focus() {
551
- var _a;
552
- if (this.controller.readonly) {
553
- return;
554
- }
555
- if (!this.isFocus) {
556
- (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
557
- setTimeout(() => {
558
- var _a, _b, _c;
559
- if (!this.nativeFocus && this.isFocus) {
560
- this.subscription.unsubscribe();
561
- (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
562
- this.subscription = new stream.Subscription();
563
- this.init();
564
- (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
612
+ getCorrectedPosition(node, offset, toAfter, excludeNodes = []) {
613
+ excludeNodes.push(node);
614
+ if (node.nodeType === Node.ELEMENT_NODE) {
615
+ const containerPosition = this.renderer.getLocationByNativeNode(node);
616
+ const childNode = node.childNodes[offset];
617
+ if (childNode) {
618
+ const childPosition = this.renderer.getLocationByNativeNode(childNode);
619
+ if (childPosition) {
620
+ if (containerPosition) {
621
+ return {
622
+ slot: childPosition.slot,
623
+ offset: childPosition.startIndex
624
+ };
625
+ }
626
+ return this.findFocusNode(childNode, toAfter, excludeNodes);
565
627
  }
566
- });
567
- }
568
- this.isFocus = true;
569
- }
570
- blur() {
571
- var _a;
572
- (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.blur();
573
- this.isFocus = false;
574
- }
575
- destroy() {
576
- this.subscription.unsubscribe();
577
- }
578
- init() {
579
- const doc = this.doc;
580
- const contentBody = doc.body;
581
- const textarea = doc.createElement('textarea');
582
- contentBody.appendChild(textarea);
583
- this.textarea = textarea;
584
- this.subscription.add(stream.fromEvent(textarea, 'blur').subscribe(() => {
585
- this.isFocus = false;
586
- this.nativeFocus = false;
587
- this.caret.hide();
588
- }), stream.fromEvent(textarea, 'focus').subscribe(() => {
589
- this.nativeFocus = true;
590
- }), this.caret.onStyleChange.subscribe(style => {
591
- Object.assign(textarea.style, style);
592
- }));
593
- Input_1.openExperimentalCompositionInput ? this.experimentalCompositionInput(textarea) : this.handleInput(textarea);
594
- this.handleShortcut(textarea);
595
- this.handleDefaultActions(textarea);
596
- }
597
- handleDefaultActions(textarea) {
598
- this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
599
- const selection = this.selection;
600
- if (!selection.isSelected) {
601
- return;
628
+ return this.findFocusNode(childNode, toAfter, excludeNodes);
602
629
  }
603
- if (selection.startSlot === selection.endSlot && selection.endOffset - selection.startOffset === 1) {
604
- const content = selection.startSlot.getContentAtIndex(selection.startOffset);
605
- if (typeof content === 'object') {
606
- const clipboardData = ev.clipboardData;
607
- const nativeSelection = document.getSelection();
608
- const range = nativeSelection.getRangeAt(0);
609
- const div = document.createElement('div');
610
- const fragment = range.cloneContents();
611
- div.append(fragment);
612
- clipboardData.setData('text/html', div.innerHTML);
613
- clipboardData.setData('text', div.innerText);
614
- ev.preventDefault();
630
+ const prevNode = node.childNodes[offset - 1];
631
+ if (prevNode) {
632
+ const prevPosition = this.renderer.getLocationByNativeNode(prevNode);
633
+ if (prevPosition && containerPosition) {
634
+ return {
635
+ slot: prevPosition.slot,
636
+ offset: prevPosition.endIndex
637
+ };
615
638
  }
616
639
  }
617
- }), stream.fromEvent(textarea, 'paste').subscribe(ev => {
618
- const text = ev.clipboardData.getData('Text');
619
- const files = Array.from(ev.clipboardData.files);
620
- if (files.length) {
621
- Promise.all(files.filter(i => {
622
- return /image/i.test(i.type);
623
- }).map(item => {
624
- const reader = new FileReader();
625
- return new Promise(resolve => {
626
- reader.onload = (event) => {
627
- resolve(event.target.result);
628
- };
629
- reader.readAsDataURL(item);
630
- });
631
- })).then(urls => {
632
- const html = urls.map(i => {
633
- return `<img src=${i}>`;
634
- }).join('');
635
- this.handlePaste(html, text);
636
- });
637
- ev.preventDefault();
638
- return;
639
- }
640
- const div = this.doc.createElement('div');
641
- div.style.cssText = 'width:1px; height:10px; overflow: hidden; position: fixed; left: 50%; top: 50%; opacity:0';
642
- div.contentEditable = 'true';
643
- this.doc.body.appendChild(div);
644
- div.focus();
645
- setTimeout(() => {
646
- const html = div.innerHTML;
647
- this.handlePaste(html, text);
648
- this.doc.body.removeChild(div);
649
- });
650
- }));
651
- }
652
- handlePaste(html, text) {
653
- const slot = this.parser.parse(html, new core.Slot([
654
- core.ContentType.BlockComponent,
655
- core.ContentType.InlineComponent,
656
- core.ContentType.Text
657
- ]));
658
- this.commander.paste(slot, text);
659
- }
660
- handleShortcut(textarea) {
661
- let isWriting = false;
662
- let isIgnore = false;
663
- this.subscription.add(stream.fromEvent(textarea, 'compositionstart').subscribe(() => {
664
- isWriting = true;
665
- }), stream.fromEvent(textarea, 'compositionend').subscribe(() => {
666
- isWriting = false;
667
- }), stream.fromEvent(textarea, 'beforeinput').subscribe(ev => {
668
- if (this.isSafari) {
669
- if (ev.inputType === 'insertFromComposition') {
670
- isIgnore = true;
671
- }
640
+ if (containerPosition) {
641
+ return {
642
+ slot: containerPosition.slot,
643
+ offset: containerPosition.endIndex
644
+ };
672
645
  }
673
- }), stream.fromEvent(textarea, 'keydown').pipe(stream.filter(() => {
674
- if (this.isSafari && isIgnore) {
675
- isIgnore = false;
676
- return false;
646
+ const nextNode = toAfter ? node.nextSibling : node.previousSibling;
647
+ if (nextNode) {
648
+ return this.findFocusNode(nextNode, toAfter, excludeNodes);
677
649
  }
678
- return !isWriting; // || !this.textarea.value
679
- })).subscribe(ev => {
680
- let key = ev.key;
681
- const b = key === 'Process' && ev.code === 'Digit2';
682
- if (b) {
683
- key = '@';
650
+ return this.findFocusNodeByParent(node, toAfter, excludeNodes);
651
+ }
652
+ else if (node.nodeType === Node.TEXT_NODE) {
653
+ const containerPosition = this.renderer.getLocationByNativeNode(node);
654
+ if (containerPosition) {
655
+ return {
656
+ slot: containerPosition.slot,
657
+ offset: containerPosition.startIndex + offset
658
+ };
684
659
  }
685
- const is = this.keyboard.execShortcut({
686
- key: key,
687
- altKey: ev.altKey,
688
- shiftKey: ev.shiftKey,
689
- ctrlKey: this.isMac ? ev.metaKey : ev.ctrlKey
690
- });
691
- if (is) {
692
- if (b) {
693
- this.isSougouPinYin = true;
694
- }
695
- ev.preventDefault();
660
+ const nextNode = toAfter ? node.nextSibling : node.previousSibling;
661
+ if (nextNode) {
662
+ return this.findFocusNode(nextNode, toAfter, excludeNodes);
696
663
  }
697
- }));
664
+ return this.findFocusNodeByParent(node, toAfter, excludeNodes);
665
+ }
666
+ return null;
698
667
  }
699
- handleInput(textarea) {
700
- this.subscription.add(stream.merge(stream.fromEvent(textarea, 'beforeinput').pipe(stream.filter(ev => {
701
- ev.preventDefault();
702
- if (this.isSafari) {
703
- return ev.inputType === 'insertText' || ev.inputType === 'insertFromComposition';
668
+ findFocusNode(node, toAfter = false, excludeNodes = []) {
669
+ if (excludeNodes.includes(node)) {
670
+ const next = toAfter ? node.nextSibling : node.previousSibling;
671
+ if (next) {
672
+ return this.findFocusNode(next, toAfter, excludeNodes);
704
673
  }
705
- return !ev.isComposing && !!ev.data;
706
- }), stream.map(ev => {
707
- return ev.data;
708
- })), this.isSafari ? new stream.Observable() : stream.fromEvent(textarea, 'compositionend').pipe(stream.map(ev => {
709
- ev.preventDefault();
710
- textarea.value = '';
711
- return ev.data;
712
- }), stream.filter(() => {
713
- const b = this.isSougouPinYin;
714
- this.isSougouPinYin = false;
715
- return !b;
716
- }))).subscribe(text => {
717
- if (text) {
718
- this.commander.write(text);
719
- }
720
- }));
721
- }
722
- experimentalCompositionInput(textarea) {
723
- let index;
724
- let offset = 0;
725
- let formats = [];
726
- this.subscription.add(stream.fromEvent(textarea, 'beforeinput').pipe(stream.filter(ev => {
727
- ev.preventDefault();
728
- if (this.isSafari) {
729
- return ev.inputType === 'insertText';
730
- // return ev.inputType === 'insertText' || ev.inputType === 'insertFromComposition'
731
- }
732
- return !ev.isComposing && !!ev.data;
733
- }), stream.map(ev => {
734
- return ev.data;
735
- })).subscribe(text => {
736
- if (text) {
737
- this.commander.write(text);
738
- }
739
- }), stream.fromEvent(textarea, 'compositionstart').subscribe(() => {
740
- const startSlot = this.selection.startSlot;
741
- formats = startSlot.extractFormatsByIndex(this.selection.startOffset);
742
- formats.push([this.inputFormatter, true]);
743
- this.scheduler.ignoreChangesTransact(() => {
744
- this.commander.write('');
745
- });
746
- index = this.selection.startOffset;
747
- }), stream.fromEvent(textarea, 'compositionupdate').subscribe((ev) => {
748
- const text = ev.data;
749
- const startSlot = this.selection.startSlot;
750
- this.selection.setBaseAndExtent(startSlot, index, startSlot, index + offset);
751
- this.scheduler.ignoreChangesTransact(() => {
752
- this.commander.insert(text, formats);
753
- });
754
- offset = text.length;
755
- }), stream.fromEvent(textarea, 'compositionend').subscribe((ev) => {
756
- textarea.value = '';
757
- const startSlot = this.selection.startSlot;
758
- this.selection.setBaseAndExtent(startSlot, index, startSlot, index + offset);
759
- this.scheduler.ignoreChangesTransact(() => {
760
- if (!this.selection.isCollapsed) {
761
- this.commander.delete();
762
- }
763
- });
764
- this.commander.insert(ev.data, formats.filter(i => i[0] !== this.inputFormatter));
765
- offset = 0;
766
- }));
674
+ return this.findFocusNodeByParent(node, toAfter, excludeNodes);
675
+ }
676
+ excludeNodes.push(node);
677
+ const position = this.renderer.getLocationByNativeNode(node);
678
+ if (position) {
679
+ return {
680
+ slot: position.slot,
681
+ offset: toAfter ? position.startIndex : position.endIndex
682
+ };
683
+ }
684
+ const firstChild = toAfter ? node.firstChild : node.lastChild;
685
+ if (firstChild) {
686
+ return this.findFocusNode(firstChild, toAfter, excludeNodes);
687
+ }
688
+ const nextSibling = toAfter ? node.nextSibling : node.previousSibling;
689
+ if (nextSibling) {
690
+ return this.findFocusNode(nextSibling, toAfter, excludeNodes);
691
+ }
692
+ return this.findFocusNodeByParent(node, toAfter, excludeNodes);
767
693
  }
768
- createEditableFrame() {
769
- return createElement('iframe', {
770
- attrs: {
771
- scrolling: 'no'
772
- },
773
- styles: {
774
- border: 'none',
775
- width: '100%',
776
- display: 'block',
777
- height: '100%',
778
- position: 'relative',
779
- top: this.isWindows ? '6px' : '0'
694
+ findFocusNodeByParent(node, toAfter, excludeNodes) {
695
+ const parentNode = node.parentNode;
696
+ if (parentNode) {
697
+ const parentPosition = this.renderer.getLocationByNativeNode(parentNode);
698
+ if (parentPosition) {
699
+ return {
700
+ slot: parentPosition.slot,
701
+ offset: toAfter ? parentPosition.endIndex : parentPosition.startIndex
702
+ };
780
703
  }
781
- });
704
+ excludeNodes.push(node);
705
+ return this.findFocusNode(parentNode, toAfter, excludeNodes);
706
+ }
707
+ return null;
782
708
  }
783
709
  };
784
- exports.Input.openExperimentalCompositionInput = false;
785
- exports.Input = Input_1 = __decorate([
710
+ exports.SelectionBridge = __decorate([
786
711
  di.Injectable(),
787
- __metadata("design:paramtypes", [exports.Parser,
788
- core.Keyboard,
789
- core.Commander,
790
- core.Selection,
712
+ __param(0, di.Inject(EDITOR_OPTIONS)),
713
+ __metadata("design:paramtypes", [Object, di.Injector,
791
714
  core.Controller,
792
- core.Scheduler,
793
- exports.Caret])
794
- ], exports.Input);
715
+ core.Selection,
716
+ core.RootComponentRef,
717
+ Input,
718
+ core.Renderer])
719
+ ], exports.SelectionBridge);
795
720
 
796
- /**
797
- * Textbus PC 端选区桥接实现
798
- */
799
- exports.SelectionBridge = class SelectionBridge {
800
- constructor(injector, caret, controller, selection, rootComponentRef, input, renderer) {
721
+ class CollaborateSelectionAwarenessDelegate {
722
+ }
723
+ exports.CollaborateCursor = class CollaborateCursor {
724
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
801
725
  this.injector = injector;
802
- this.caret = caret;
803
- this.controller = controller;
726
+ this.nativeSelection = nativeSelection;
727
+ this.scheduler = scheduler;
804
728
  this.selection = selection;
805
- this.rootComponentRef = rootComponentRef;
806
- this.input = input;
807
- this.renderer = renderer;
808
- this.nativeSelection = document.getSelection();
809
- this.selectionMaskElement = createElement('style');
810
- this.selectionChangeEvent = new stream.Subject();
811
- this.subs = [];
812
- this.connector = null;
813
- this.ignoreSelectionChange = false;
814
- this.changeFromUser = false;
815
- this.docContainer = injector.get(VIEW_DOCUMENT);
816
- this.maskContainer = injector.get(VIEW_MASK);
817
- this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(stream.filter(() => {
818
- return !controller.readonly;
819
- }));
820
- document.head.appendChild(this.selectionMaskElement);
821
- this.sub = this.onSelectionChange.subscribe((r) => {
822
- if (r) {
823
- this.caret.show(r, this.changeFromUser);
824
- }
825
- else {
826
- this.caret.hide();
729
+ this.awarenessDelegate = awarenessDelegate;
730
+ this.host = createElement('div', {
731
+ styles: {
732
+ position: 'absolute',
733
+ left: 0,
734
+ top: 0,
735
+ width: '100%',
736
+ height: '100%',
737
+ pointerEvents: 'none',
738
+ zIndex: 1
827
739
  }
828
- if (r) {
829
- input.focus();
740
+ });
741
+ this.canvasContainer = createElement('div', {
742
+ styles: {
743
+ position: 'absolute',
744
+ left: 0,
745
+ top: 0,
746
+ width: '100%',
747
+ height: '100%',
748
+ overflow: 'hidden'
830
749
  }
831
- else {
832
- input.blur();
750
+ });
751
+ this.canvas = createElement('canvas', {
752
+ styles: {
753
+ position: 'absolute',
754
+ opacity: 0.5,
755
+ left: 0,
756
+ top: 0,
757
+ width: '100%',
758
+ height: document.documentElement.clientHeight + 'px',
759
+ pointerEvents: 'none',
833
760
  }
834
761
  });
835
- this.sub.add(stream.fromEvent(document, 'focusin').subscribe(ev => {
836
- let target = ev.target;
837
- if (/^(input|textarea|select)$/i.test(target.nodeName)) {
838
- if (target.tagName.toLowerCase() === 'input' && /^(range|date)$/.test(target.type)) {
839
- return;
840
- }
841
- this.ignoreSelectionChange = true;
842
- return;
762
+ this.context = this.canvas.getContext('2d');
763
+ this.tooltips = createElement('div', {
764
+ styles: {
765
+ position: 'absolute',
766
+ left: 0,
767
+ top: 0,
768
+ width: '100%',
769
+ height: '100%',
770
+ pointerEvents: 'none',
771
+ fontSize: '12px',
772
+ zIndex: 10
843
773
  }
844
- while (target) {
845
- if (target.contentEditable === 'true') {
846
- this.ignoreSelectionChange = true;
847
- return;
848
- }
849
- target = target.parentNode;
774
+ });
775
+ this.onRectsChange = new stream.Subject();
776
+ this.subscription = new stream.Subscription();
777
+ this.currentSelection = [];
778
+ this.container = injector.get(VIEW_CONTAINER);
779
+ this.canvasContainer.append(this.canvas);
780
+ this.host.append(this.canvasContainer, this.tooltips);
781
+ this.container.prepend(this.host);
782
+ this.subscription.add(this.onRectsChange.subscribe(rects => {
783
+ for (const rect of rects) {
784
+ this.context.fillStyle = rect.color;
785
+ this.context.beginPath();
786
+ this.context.rect(rect.left, rect.top, rect.width, rect.height);
787
+ this.context.fill();
788
+ this.context.closePath();
850
789
  }
790
+ }), stream.fromEvent(window, 'resize').subscribe(() => {
791
+ this.canvas.style.height = document.documentElement.clientHeight + 'px';
792
+ this.refresh();
793
+ }), this.scheduler.onDocChanged.subscribe(() => {
794
+ this.refresh();
851
795
  }));
852
- this.sub.add(stream.fromEvent(document, 'focusout').subscribe(() => {
853
- this.ignoreSelectionChange = false;
854
- }));
855
- }
856
- connect(connector) {
857
- this.disConnect();
858
- this.connector = connector;
859
- this.syncSelection(connector);
860
- this.listen(connector);
861
- }
862
- disConnect() {
863
- this.connector = null;
864
- this.unListen();
865
- }
866
- getRect(location) {
867
- const { focus, anchor } = this.getPositionByRange({
868
- focusOffset: location.offset,
869
- anchorOffset: location.offset,
870
- focusSlot: location.slot,
871
- anchorSlot: location.slot
872
- });
873
- if (!focus || !anchor) {
874
- return null;
875
- }
876
- const nativeRange = document.createRange();
877
- nativeRange.setStart(focus.node, focus.offset);
878
- nativeRange.collapse();
879
- return getLayoutRectByRange(nativeRange);
880
796
  }
881
- restore(abstractSelection, formLocal) {
882
- this.changeFromUser = formLocal;
883
- if (this.ignoreSelectionChange || !this.connector) {
884
- return;
885
- }
886
- this.unListen();
887
- if (!abstractSelection) {
888
- this.nativeSelection.removeAllRanges();
889
- this.selectionChangeEvent.next(null);
890
- this.listen(this.connector);
891
- return;
892
- }
893
- const { focus, anchor } = this.getPositionByRange(abstractSelection);
894
- if (!focus || !anchor) {
895
- this.nativeSelection.removeAllRanges();
896
- this.selectionChangeEvent.next(null);
897
- this.listen(this.connector);
898
- return;
899
- }
900
- this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
901
- if (this.nativeSelection.rangeCount) {
902
- const nativeRange = this.nativeSelection.getRangeAt(0);
903
- this.selectionChangeEvent.next(nativeRange);
904
- }
905
- else {
906
- this.selectionChangeEvent.next(null);
907
- }
908
- // hack start 浏览器会触发上面选区更改事件
909
- const bind = () => {
910
- if (this.connector) {
911
- this.listen(this.connector);
912
- }
913
- };
914
- if (typeof requestIdleCallback === 'function') {
915
- requestIdleCallback(bind);
916
- }
917
- else {
918
- setTimeout(bind, 30);
919
- }
920
- // hack end
797
+ refresh() {
798
+ this.draw(this.currentSelection);
921
799
  }
922
800
  destroy() {
923
- this.caret.destroy();
924
- this.sub.unsubscribe();
925
- }
926
- getPositionByRange(abstractSelection) {
927
- let focus;
928
- let anchor;
929
- try {
930
- focus = this.findSelectedNodeAndOffset(abstractSelection.focusSlot, abstractSelection.focusOffset);
931
- anchor = focus;
932
- if (abstractSelection.anchorSlot !== abstractSelection.focusSlot ||
933
- abstractSelection.anchorOffset !== abstractSelection.focusOffset) {
934
- anchor = this.findSelectedNodeAndOffset(abstractSelection.anchorSlot, abstractSelection.anchorOffset);
935
- }
936
- return {
937
- focus,
938
- anchor
939
- };
940
- }
941
- catch (e) {
942
- return {
943
- focus: null,
944
- anchor: null
945
- };
946
- }
947
- }
948
- getPreviousLinePositionByCurrent(position) {
949
- return this.getLinePosition(position, false);
950
- }
951
- getNextLinePositionByCurrent(position) {
952
- return this.getLinePosition(position, true);
953
- }
954
- getLinePosition(currentPosition, toNext) {
955
- clearTimeout(this.cacheCaretPositionTimer);
956
- let p;
957
- if (this.oldCaretPosition) {
958
- p = toNext ?
959
- this.getNextLinePositionByOffset(currentPosition, this.oldCaretPosition.left) :
960
- this.getPreviousLinePositionByOffset(currentPosition, this.oldCaretPosition.left);
961
- }
962
- else {
963
- this.oldCaretPosition = this.getRect(currentPosition);
964
- p = toNext ?
965
- this.getNextLinePositionByOffset(currentPosition, this.oldCaretPosition.left) :
966
- this.getPreviousLinePositionByOffset(currentPosition, this.oldCaretPosition.left);
967
- }
968
- this.cacheCaretPositionTimer = setTimeout(() => {
969
- this.oldCaretPosition = null;
970
- }, 3000);
971
- return p;
801
+ this.subscription.unsubscribe();
972
802
  }
973
- /**
974
- * 获取选区向上移动一行的位置。
975
- * @param currentPosition
976
- * @param startLeft 参考位置。
977
- */
978
- getPreviousLinePositionByOffset(currentPosition, startLeft) {
979
- let isToPrevLine = false;
980
- let loopCount = 0;
981
- let minLeft = startLeft;
982
- let focusSlot = currentPosition.slot;
983
- let focusOffset = currentPosition.offset;
984
- let minTop = this.getRect({
985
- slot: focusSlot,
986
- offset: focusOffset
987
- }).top;
988
- let position;
989
- let oldPosition;
990
- let oldLeft = 0;
991
- while (true) {
992
- loopCount++;
993
- position = this.selection.getPreviousPositionByPosition(focusSlot, focusOffset);
994
- focusSlot = position.slot;
995
- focusOffset = position.offset;
996
- const rect2 = this.getRect(position);
997
- if (!isToPrevLine) {
998
- if (rect2.left > minLeft || rect2.top < minTop) {
999
- isToPrevLine = true;
1000
- }
1001
- else if (rect2.left === minLeft && rect2.top === minTop) {
1002
- return position;
1003
- }
1004
- minLeft = rect2.left;
1005
- minTop = rect2.top;
803
+ draw(paths) {
804
+ this.currentSelection = paths;
805
+ const containerRect = this.container.getBoundingClientRect();
806
+ this.canvas.style.top = containerRect.top * -1 + 'px';
807
+ this.canvas.width = this.canvas.offsetWidth;
808
+ this.canvas.height = this.canvas.offsetHeight;
809
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
810
+ const users = [];
811
+ paths.filter(i => {
812
+ return i.paths.anchor.length && i.paths.focus.length;
813
+ }).forEach(item => {
814
+ const anchorPaths = [...item.paths.anchor];
815
+ const focusPaths = [...item.paths.focus];
816
+ const anchorOffset = anchorPaths.pop();
817
+ const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
818
+ const focusOffset = focusPaths.pop();
819
+ const focusSlot = this.selection.findSlotByPaths(focusPaths);
820
+ if (!anchorSlot || !focusSlot) {
821
+ return;
1006
822
  }
1007
- if (isToPrevLine) {
1008
- if (rect2.left < startLeft) {
1009
- return position;
1010
- }
1011
- if (oldPosition) {
1012
- if (rect2.left >= oldLeft) {
1013
- return oldPosition;
1014
- }
1015
- }
1016
- oldLeft = rect2.left;
1017
- oldPosition = position;
823
+ const { focus, anchor } = this.nativeSelection.getPositionByRange({
824
+ focusOffset,
825
+ anchorOffset,
826
+ focusSlot,
827
+ anchorSlot
828
+ });
829
+ if (!focus || !anchor) {
830
+ return;
1018
831
  }
1019
- if (loopCount > 10000) {
1020
- break;
832
+ const nativeRange = document.createRange();
833
+ nativeRange.setStart(anchor.node, anchor.offset);
834
+ nativeRange.setEnd(focus.node, focus.offset);
835
+ if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
836
+ nativeRange.setStart(focus.node, focus.offset);
837
+ nativeRange.setEnd(anchor.node, anchor.offset);
1021
838
  }
1022
- }
1023
- return position || {
1024
- offset: 0,
1025
- slot: focusSlot
1026
- };
1027
- }
1028
- /**
1029
- * 获取选区向下移动一行的位置。
1030
- * @param currentPosition
1031
- * @param startLeft 参考位置。
1032
- */
1033
- getNextLinePositionByOffset(currentPosition, startLeft) {
1034
- let isToNextLine = false;
1035
- let loopCount = 0;
1036
- let maxRight = startLeft;
1037
- let focusSlot = currentPosition.slot;
1038
- let focusOffset = currentPosition.offset;
1039
- let minTop = this.getRect({
1040
- slot: focusSlot,
1041
- offset: focusOffset
1042
- }).top;
1043
- let oldPosition;
1044
- let oldLeft = 0;
1045
- while (true) {
1046
- loopCount++;
1047
- const position = this.selection.getNextPositionByPosition(focusSlot, focusOffset);
1048
- focusSlot = position.slot;
1049
- focusOffset = position.offset;
1050
- const rect2 = this.getRect(position);
1051
- if (!isToNextLine) {
1052
- if (rect2.left < maxRight || rect2.top > minTop) {
1053
- isToNextLine = true;
1054
- }
1055
- else if (rect2.left === maxRight && rect2.top === minTop) {
1056
- return position;
1057
- }
1058
- maxRight = rect2.left;
1059
- minTop = rect2.top;
1060
- oldPosition = position;
839
+ let rects = false;
840
+ if (this.awarenessDelegate) {
841
+ rects = this.awarenessDelegate.getRects({
842
+ focusOffset,
843
+ anchorOffset,
844
+ focusSlot,
845
+ anchorSlot
846
+ }, nativeRange);
1061
847
  }
1062
- if (isToNextLine) {
1063
- if (rect2.left > startLeft) {
1064
- return oldPosition;
1065
- }
1066
- if (oldPosition) {
1067
- if (rect2.left <= oldLeft) {
1068
- return oldPosition;
1069
- }
1070
- }
1071
- oldPosition = position;
1072
- oldLeft = rect2.left;
848
+ if (!rects) {
849
+ rects = nativeRange.getClientRects();
1073
850
  }
1074
- if (loopCount > 10000) {
1075
- break;
851
+ const selectionRects = [];
852
+ for (let i = rects.length - 1; i >= 0; i--) {
853
+ const rect = rects[i];
854
+ selectionRects.push({
855
+ id: item.id,
856
+ color: item.color,
857
+ username: item.username,
858
+ left: rect.left - containerRect.left,
859
+ top: rect.top,
860
+ width: rect.width,
861
+ height: rect.height,
862
+ });
1076
863
  }
1077
- }
1078
- return oldPosition || {
1079
- offset: focusSlot.length,
1080
- slot: focusSlot
1081
- };
1082
- }
1083
- unListen() {
1084
- this.subs.forEach(i => i.unsubscribe());
1085
- this.subs = [];
1086
- }
1087
- listen(connector) {
1088
- const selection = this.nativeSelection;
1089
- this.subs.push(stream.fromEvent(this.docContainer, 'mousedown').subscribe(ev => {
1090
- if (this.ignoreSelectionChange || ev.button === 2) {
864
+ this.onRectsChange.next(selectionRects);
865
+ const cursorRange = nativeRange.cloneRange();
866
+ cursorRange.setStart(focus.node, focus.offset);
867
+ cursorRange.collapse(true);
868
+ const cursorRect = getLayoutRectByRange(cursorRange);
869
+ const rect = {
870
+ id: item.id,
871
+ username: item.username,
872
+ color: item.color,
873
+ left: cursorRect.left - containerRect.left,
874
+ top: cursorRect.top - containerRect.top,
875
+ width: 1,
876
+ height: cursorRect.height
877
+ };
878
+ if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
1091
879
  return;
1092
880
  }
1093
- if (!ev.shiftKey) {
1094
- selection.removeAllRanges();
1095
- }
1096
- }), stream.fromEvent(document, 'selectionchange').subscribe(() => {
1097
- this.syncSelection(connector);
1098
- }));
881
+ users.push(rect);
882
+ });
883
+ this.drawUserCursor(users);
1099
884
  }
1100
- syncSelection(connector) {
1101
- var _a;
1102
- const selection = this.nativeSelection;
1103
- this.changeFromUser = true;
1104
- if (this.ignoreSelectionChange ||
1105
- selection.rangeCount === 0 ||
1106
- !this.docContainer.contains(selection.anchorNode)) {
1107
- return;
1108
- }
1109
- const nativeRange = selection.getRangeAt(0).cloneRange();
1110
- const isFocusEnd = selection.focusNode === nativeRange.endContainer && selection.focusOffset === nativeRange.endOffset;
1111
- const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
1112
- if (!this.docContainer.contains(selection.focusNode)) {
1113
- if (isFocusEnd) {
1114
- const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.first);
1115
- const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
1116
- nativeRange.setEndAfter(nativeNode.lastChild);
1117
- }
1118
- else {
1119
- const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.last);
1120
- const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
1121
- nativeRange.setStartBefore(nativeNode.firstChild);
1122
- }
1123
- }
1124
- const startPosition = this.getCorrectedPosition(nativeRange.startContainer, nativeRange.startOffset, isFocusStart);
1125
- const endPosition = nativeRange.collapsed ?
1126
- startPosition :
1127
- this.getCorrectedPosition(nativeRange.endContainer, nativeRange.endOffset, isFocusEnd);
1128
- if ([Node.ELEMENT_NODE, Node.TEXT_NODE].includes((_a = nativeRange.commonAncestorContainer) === null || _a === void 0 ? void 0 : _a.nodeType) &&
1129
- startPosition && endPosition) {
1130
- const abstractSelection = isFocusEnd ? {
1131
- anchorSlot: startPosition.slot,
1132
- anchorOffset: startPosition.offset,
1133
- focusSlot: endPosition.slot,
1134
- focusOffset: endPosition.offset
1135
- } : {
1136
- focusSlot: startPosition.slot,
1137
- focusOffset: startPosition.offset,
1138
- anchorSlot: endPosition.slot,
1139
- anchorOffset: endPosition.offset
1140
- };
1141
- const { focus, anchor } = this.getPositionByRange(abstractSelection);
1142
- if (focus && anchor) {
1143
- let start = anchor;
1144
- let end = focus;
1145
- if (isFocusStart) {
1146
- start = focus;
1147
- end = anchor;
1148
- }
1149
- if (nativeRange.startContainer !== start.node || nativeRange.startOffset !== start.offset) {
1150
- nativeRange.setStart(start.node, start.offset);
1151
- }
1152
- if (nativeRange.endContainer !== end.node || nativeRange.endOffset !== end.offset) {
1153
- nativeRange.setEnd(end.node, end.offset);
1154
- }
1155
- connector.setSelection(abstractSelection);
1156
- this.selectionChangeEvent.next(nativeRange);
1157
- }
1158
- else {
1159
- connector.setSelection(null);
1160
- }
1161
- return;
1162
- }
1163
- connector.setSelection(null);
1164
- }
1165
- findSelectedNodeAndOffset(slot, offset) {
1166
- const prev = slot.getContentAtIndex(offset - 1);
1167
- const vNodes = this.renderer.getVNodesBySlot(slot);
1168
- if (prev) {
1169
- if (typeof prev !== 'string') {
1170
- const vNode = this.renderer.getVNodeByComponent(prev);
1171
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1172
- return {
1173
- node: nativeNode.parentNode,
1174
- offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode) + 1
1175
- };
1176
- }
1177
- else if (prev === '\n') {
1178
- for (const vNode of vNodes) {
1179
- if (vNode instanceof core.VTextNode) {
1180
- continue;
1181
- }
1182
- if (vNode.tagName === 'br') {
1183
- const position = this.renderer.getLocationByVNode(vNode);
1184
- if (position) {
1185
- if (position.endIndex === offset) {
1186
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1187
- const parentNode = nativeNode.parentNode;
1188
- return {
1189
- node: parentNode,
1190
- offset: Array.from(parentNode.childNodes).indexOf(nativeNode) + 1
1191
- };
1192
- }
1193
- }
1194
- }
1195
- }
1196
- }
1197
- }
1198
- const current = slot.getContentAtIndex(offset);
1199
- if (current && typeof current !== 'string') {
1200
- const vNode = this.renderer.getVNodeByComponent(current);
1201
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1202
- return {
1203
- node: nativeNode.parentNode,
1204
- offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
1205
- };
1206
- }
1207
- for (const vNode of vNodes) {
1208
- if (vNode instanceof core.VElement) {
1209
- if (vNode.tagName === 'br') {
1210
- const position = this.renderer.getLocationByVNode(vNode);
1211
- if (position) {
1212
- if (position.startIndex === offset) {
1213
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1214
- const parentNode = nativeNode.parentNode;
1215
- return {
1216
- node: parentNode,
1217
- offset: Array.from(parentNode.childNodes).indexOf(nativeNode)
1218
- };
1219
- }
1220
- }
1221
- }
1222
- continue;
1223
- }
1224
- const position = this.renderer.getLocationByVNode(vNode);
1225
- if (position) {
1226
- if (offset >= position.startIndex && offset <= position.endIndex) {
1227
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1228
- return {
1229
- node: nativeNode,
1230
- offset: offset - position.startIndex
1231
- };
1232
- }
1233
- }
1234
- }
1235
- return null;
1236
- }
1237
- getCorrectedPosition(node, offset, toAfter, excludeNodes = []) {
1238
- excludeNodes.push(node);
1239
- if (node.nodeType === Node.ELEMENT_NODE) {
1240
- const containerPosition = this.renderer.getLocationByNativeNode(node);
1241
- const childNode = node.childNodes[offset];
1242
- if (childNode) {
1243
- const childPosition = this.renderer.getLocationByNativeNode(childNode);
1244
- if (childPosition) {
1245
- if (containerPosition) {
1246
- return {
1247
- slot: childPosition.slot,
1248
- offset: childPosition.startIndex
1249
- };
1250
- }
1251
- return this.findFocusNode(childNode, toAfter, excludeNodes);
1252
- }
1253
- return this.findFocusNode(childNode, toAfter, excludeNodes);
1254
- }
1255
- const prevNode = node.childNodes[offset - 1];
1256
- if (prevNode) {
1257
- const prevPosition = this.renderer.getLocationByNativeNode(prevNode);
1258
- if (prevPosition && containerPosition) {
1259
- return {
1260
- slot: prevPosition.slot,
1261
- offset: prevPosition.endIndex
1262
- };
1263
- }
1264
- }
1265
- if (containerPosition) {
1266
- return {
1267
- slot: containerPosition.slot,
1268
- offset: containerPosition.endIndex
1269
- };
1270
- }
1271
- const nextNode = toAfter ? node.nextSibling : node.previousSibling;
1272
- if (nextNode) {
1273
- return this.findFocusNode(nextNode, toAfter, excludeNodes);
1274
- }
1275
- return this.findFocusNodeByParent(node, toAfter, excludeNodes);
885
+ drawUserCursor(rects) {
886
+ for (let i = 0; i < rects.length; i++) {
887
+ const rect = rects[i];
888
+ const { cursor, userTip, anchor } = this.getUserCursor(i);
889
+ Object.assign(cursor.style, {
890
+ left: rect.left + 'px',
891
+ top: rect.top + 'px',
892
+ width: rect.width + 'px',
893
+ height: rect.height + 'px',
894
+ background: rect.color,
895
+ display: 'block'
896
+ });
897
+ anchor.style.background = rect.color;
898
+ userTip.innerText = rect.username;
899
+ userTip.style.background = rect.color;
1276
900
  }
1277
- else if (node.nodeType === Node.TEXT_NODE) {
1278
- const containerPosition = this.renderer.getLocationByNativeNode(node);
1279
- if (containerPosition) {
1280
- return {
1281
- slot: containerPosition.slot,
1282
- offset: containerPosition.startIndex + offset
1283
- };
1284
- }
1285
- const nextNode = toAfter ? node.nextSibling : node.previousSibling;
1286
- if (nextNode) {
1287
- return this.findFocusNode(nextNode, toAfter, excludeNodes);
1288
- }
1289
- return this.findFocusNodeByParent(node, toAfter, excludeNodes);
901
+ for (let i = rects.length; i < this.tooltips.children.length; i++) {
902
+ this.tooltips.removeChild(this.tooltips.children[i]);
1290
903
  }
1291
- return null;
1292
904
  }
1293
- findFocusNode(node, toAfter = false, excludeNodes = []) {
1294
- if (excludeNodes.includes(node)) {
1295
- const next = toAfter ? node.nextSibling : node.previousSibling;
1296
- if (next) {
1297
- return this.findFocusNode(next, toAfter, excludeNodes);
1298
- }
1299
- return this.findFocusNodeByParent(node, toAfter, excludeNodes);
1300
- }
1301
- excludeNodes.push(node);
1302
- const position = this.renderer.getLocationByNativeNode(node);
1303
- if (position) {
905
+ getUserCursor(index) {
906
+ let child = this.tooltips.children[index];
907
+ if (child) {
908
+ const anchor = child.children[0];
1304
909
  return {
1305
- slot: position.slot,
1306
- offset: toAfter ? position.startIndex : position.endIndex
910
+ cursor: child,
911
+ anchor,
912
+ userTip: anchor.children[0]
1307
913
  };
1308
914
  }
1309
- const firstChild = toAfter ? node.firstChild : node.lastChild;
1310
- if (firstChild) {
1311
- return this.findFocusNode(firstChild, toAfter, excludeNodes);
1312
- }
1313
- const nextSibling = toAfter ? node.nextSibling : node.previousSibling;
1314
- if (nextSibling) {
1315
- return this.findFocusNode(nextSibling, toAfter, excludeNodes);
1316
- }
1317
- return this.findFocusNodeByParent(node, toAfter, excludeNodes);
1318
- }
1319
- findFocusNodeByParent(node, toAfter, excludeNodes) {
1320
- const parentNode = node.parentNode;
1321
- if (parentNode) {
1322
- const parentPosition = this.renderer.getLocationByNativeNode(parentNode);
1323
- if (parentPosition) {
1324
- return {
1325
- slot: parentPosition.slot,
1326
- offset: toAfter ? parentPosition.endIndex : parentPosition.startIndex
1327
- };
1328
- }
1329
- excludeNodes.push(node);
1330
- return this.findFocusNode(parentNode, toAfter, excludeNodes);
1331
- }
1332
- return null;
1333
- }
1334
- };
1335
- exports.SelectionBridge = __decorate([
1336
- di.Injectable(),
1337
- __metadata("design:paramtypes", [di.Injector,
1338
- exports.Caret,
1339
- core.Controller,
1340
- core.Selection,
1341
- core.RootComponentRef,
1342
- exports.Input,
1343
- core.Renderer])
1344
- ], exports.SelectionBridge);
1345
-
1346
- class CollaborateSelectionAwarenessDelegate {
1347
- }
1348
- exports.CollaborateCursor = class CollaborateCursor {
1349
- constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
1350
- this.injector = injector;
1351
- this.nativeSelection = nativeSelection;
1352
- this.scheduler = scheduler;
1353
- this.selection = selection;
1354
- this.awarenessDelegate = awarenessDelegate;
1355
- this.host = createElement('div', {
1356
- styles: {
1357
- position: 'absolute',
1358
- left: 0,
1359
- top: 0,
1360
- width: '100%',
1361
- height: '100%',
1362
- pointerEvents: 'none',
1363
- zIndex: 1
1364
- }
1365
- });
1366
- this.canvasContainer = createElement('div', {
1367
- styles: {
1368
- position: 'absolute',
1369
- left: 0,
1370
- top: 0,
1371
- width: '100%',
1372
- height: '100%',
1373
- overflow: 'hidden'
1374
- }
1375
- });
1376
- this.canvas = createElement('canvas', {
1377
- styles: {
1378
- position: 'absolute',
1379
- opacity: 0.5,
1380
- left: 0,
1381
- top: 0,
1382
- width: '100%',
1383
- height: document.documentElement.clientHeight + 'px',
1384
- pointerEvents: 'none',
1385
- }
1386
- });
1387
- this.context = this.canvas.getContext('2d');
1388
- this.tooltips = createElement('div', {
1389
- styles: {
1390
- position: 'absolute',
1391
- left: 0,
1392
- top: 0,
1393
- width: '100%',
1394
- height: '100%',
1395
- pointerEvents: 'none',
1396
- fontSize: '12px',
1397
- zIndex: 10
1398
- }
1399
- });
1400
- this.onRectsChange = new stream.Subject();
1401
- this.subscription = new stream.Subscription();
1402
- this.currentSelection = [];
1403
- this.container = injector.get(VIEW_CONTAINER);
1404
- this.canvasContainer.append(this.canvas);
1405
- this.host.append(this.canvasContainer, this.tooltips);
1406
- this.container.prepend(this.host);
1407
- this.subscription.add(this.onRectsChange.subscribe(rects => {
1408
- for (const rect of rects) {
1409
- this.context.fillStyle = rect.color;
1410
- this.context.beginPath();
1411
- this.context.rect(rect.left, rect.top, rect.width, rect.height);
1412
- this.context.fill();
1413
- this.context.closePath();
1414
- }
1415
- }), stream.fromEvent(window, 'resize').subscribe(() => {
1416
- this.canvas.style.height = document.documentElement.clientHeight + 'px';
1417
- this.refresh();
1418
- }), this.scheduler.onDocChanged.subscribe(() => {
1419
- this.refresh();
1420
- }));
1421
- }
1422
- refresh() {
1423
- this.draw(this.currentSelection);
1424
- }
1425
- destroy() {
1426
- this.subscription.unsubscribe();
1427
- }
1428
- draw(paths) {
1429
- this.currentSelection = paths;
1430
- const containerRect = this.container.getBoundingClientRect();
1431
- this.canvas.style.top = containerRect.top * -1 + 'px';
1432
- this.canvas.width = this.canvas.offsetWidth;
1433
- this.canvas.height = this.canvas.offsetHeight;
1434
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1435
- const users = [];
1436
- paths.filter(i => {
1437
- return i.paths.anchor.length && i.paths.focus.length;
1438
- }).forEach(item => {
1439
- const anchorPaths = [...item.paths.anchor];
1440
- const focusPaths = [...item.paths.focus];
1441
- const anchorOffset = anchorPaths.pop();
1442
- const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1443
- const focusOffset = focusPaths.pop();
1444
- const focusSlot = this.selection.findSlotByPaths(focusPaths);
1445
- if (!anchorSlot || !focusSlot) {
1446
- return;
1447
- }
1448
- const { focus, anchor } = this.nativeSelection.getPositionByRange({
1449
- focusOffset,
1450
- anchorOffset,
1451
- focusSlot,
1452
- anchorSlot
1453
- });
1454
- if (!focus || !anchor) {
1455
- return;
1456
- }
1457
- const nativeRange = document.createRange();
1458
- nativeRange.setStart(anchor.node, anchor.offset);
1459
- nativeRange.setEnd(focus.node, focus.offset);
1460
- if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1461
- nativeRange.setStart(focus.node, focus.offset);
1462
- nativeRange.setEnd(anchor.node, anchor.offset);
1463
- }
1464
- let rects = false;
1465
- if (this.awarenessDelegate) {
1466
- rects = this.awarenessDelegate.getRects({
1467
- focusOffset,
1468
- anchorOffset,
1469
- focusSlot,
1470
- anchorSlot
1471
- }, nativeRange);
1472
- }
1473
- if (!rects) {
1474
- rects = nativeRange.getClientRects();
1475
- }
1476
- const selectionRects = [];
1477
- for (let i = rects.length - 1; i >= 0; i--) {
1478
- const rect = rects[i];
1479
- selectionRects.push({
1480
- id: item.id,
1481
- color: item.color,
1482
- username: item.username,
1483
- left: rect.left - containerRect.left,
1484
- top: rect.top,
1485
- width: rect.width,
1486
- height: rect.height,
1487
- });
1488
- }
1489
- this.onRectsChange.next(selectionRects);
1490
- const cursorRange = nativeRange.cloneRange();
1491
- cursorRange.setStart(focus.node, focus.offset);
1492
- cursorRange.collapse(true);
1493
- const cursorRect = getLayoutRectByRange(cursorRange);
1494
- const rect = {
1495
- id: item.id,
1496
- username: item.username,
1497
- color: item.color,
1498
- left: cursorRect.left - containerRect.left,
1499
- top: cursorRect.top - containerRect.top,
1500
- width: 1,
1501
- height: cursorRect.height
1502
- };
1503
- if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
1504
- return;
1505
- }
1506
- users.push(rect);
1507
- });
1508
- this.drawUserCursor(users);
1509
- }
1510
- drawUserCursor(rects) {
1511
- for (let i = 0; i < rects.length; i++) {
1512
- const rect = rects[i];
1513
- const { cursor, userTip, anchor } = this.getUserCursor(i);
1514
- Object.assign(cursor.style, {
1515
- left: rect.left + 'px',
1516
- top: rect.top + 'px',
1517
- width: rect.width + 'px',
1518
- height: rect.height + 'px',
1519
- background: rect.color,
1520
- display: 'block'
1521
- });
1522
- anchor.style.background = rect.color;
1523
- userTip.innerText = rect.username;
1524
- userTip.style.background = rect.color;
1525
- }
1526
- for (let i = rects.length; i < this.tooltips.children.length; i++) {
1527
- this.tooltips.removeChild(this.tooltips.children[i]);
1528
- }
1529
- }
1530
- getUserCursor(index) {
1531
- let child = this.tooltips.children[index];
1532
- if (child) {
1533
- const anchor = child.children[0];
1534
- return {
1535
- cursor: child,
1536
- anchor,
1537
- userTip: anchor.children[0]
1538
- };
1539
- }
1540
- const userTip = createElement('span', {
915
+ const userTip = createElement('span', {
1541
916
  styles: {
1542
917
  position: 'absolute',
1543
918
  left: '50%',
@@ -1714,71 +1089,917 @@ exports.DomRenderer = DomRenderer_1 = class DomRenderer {
1714
1089
  appendChild(parent, newChild) {
1715
1090
  parent.appendChild(newChild);
1716
1091
  }
1717
- remove(node) {
1718
- node.parentNode.removeChild(node);
1092
+ remove(node) {
1093
+ var _a;
1094
+ (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
1095
+ }
1096
+ insertBefore(newNode, ref) {
1097
+ ref.parentNode.insertBefore(newNode, ref);
1098
+ }
1099
+ getChildByIndex(parent, index) {
1100
+ return parent.childNodes[index] || null;
1101
+ }
1102
+ addClass(target, name) {
1103
+ target.classList.add(name);
1104
+ }
1105
+ removeClass(target, name) {
1106
+ target.classList.remove(name);
1107
+ }
1108
+ setStyle(target, key, value) {
1109
+ target.style[key] = value !== null && value !== void 0 ? value : '';
1110
+ }
1111
+ syncTextContent(target, content) {
1112
+ if (target.textContent !== content) {
1113
+ target.textContent = content;
1114
+ }
1115
+ }
1116
+ removeStyle(target, key) {
1117
+ target.style[key] = '';
1118
+ }
1119
+ setAttribute(target, key, value) {
1120
+ if (this.possibleXlinkNames[key]) {
1121
+ this.setXlinkAttribute(target, this.possibleXlinkNames[key], value);
1122
+ return;
1123
+ }
1124
+ target.setAttribute(key, value);
1125
+ const propNames = this.formElement[target.tagName.toLowerCase()];
1126
+ if (propNames && propNames.includes(key)) {
1127
+ target[key] = Boolean(value);
1128
+ }
1129
+ }
1130
+ removeAttribute(target, key) {
1131
+ if (this.possibleXlinkNames[key]) {
1132
+ this.removeXlinkAttribute(target, this.possibleXlinkNames[key]);
1133
+ }
1134
+ target.removeAttribute(key);
1135
+ const propNames = this.formElement[target.tagName.toLowerCase()];
1136
+ if (propNames && propNames.includes(key)) {
1137
+ target[key] = false;
1138
+ }
1139
+ }
1140
+ setXlinkAttribute(target, key, value) {
1141
+ target.setAttributeNS(this.xlinkNameSpace, key, value);
1142
+ }
1143
+ removeXlinkAttribute(target, key) {
1144
+ target.removeAttributeNS(this.xlinkNameSpace, key);
1145
+ }
1146
+ replace(newChild, oldChild) {
1147
+ oldChild.parentNode.replaceChild(newChild, oldChild);
1148
+ }
1149
+ copy() {
1150
+ document.execCommand('copy');
1151
+ }
1152
+ static replaceEmpty(s, target) {
1153
+ return s.replace(/\s\s+/g, str => {
1154
+ return ' ' + Array.from({
1155
+ length: str.length - 1
1156
+ }).fill(target).join('');
1157
+ }).replace(/^\s|\s$/g, target);
1158
+ }
1159
+ };
1160
+ exports.DomRenderer = DomRenderer_1 = __decorate([
1161
+ di.Injectable()
1162
+ ], exports.DomRenderer);
1163
+
1164
+ var Parser_1;
1165
+ exports.Parser = Parser_1 = class Parser {
1166
+ constructor(options, injector) {
1167
+ var _a;
1168
+ this.options = options;
1169
+ this.injector = injector;
1170
+ const componentLoaders = [
1171
+ ...(options.componentLoaders || [])
1172
+ ];
1173
+ const formatLoaders = [
1174
+ ...(options.formatLoaders || [])
1175
+ ];
1176
+ const attributeLoaders = [
1177
+ ...(options.attributeLoaders || [])
1178
+ ];
1179
+ (_a = options.imports) === null || _a === void 0 ? void 0 : _a.forEach(i => {
1180
+ componentLoaders.push(...(i.componentLoaders || []));
1181
+ formatLoaders.push(...(i.formatLoaders || []));
1182
+ });
1183
+ this.componentLoaders = componentLoaders;
1184
+ this.formatLoaders = formatLoaders;
1185
+ this.attributeLoaders = attributeLoaders;
1186
+ }
1187
+ static parseHTML(html) {
1188
+ return new DOMParser().parseFromString(html, 'text/html').body;
1189
+ }
1190
+ parseDoc(html, rootComponentLoader) {
1191
+ const element = Parser_1.parseHTML(html);
1192
+ return rootComponentLoader.read(element, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
1193
+ return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
1194
+ });
1195
+ }
1196
+ parse(html, rootSlot) {
1197
+ const element = Parser_1.parseHTML(html);
1198
+ const formatItems = this.readFormats(element, rootSlot, []);
1199
+ this.applyFormats(rootSlot, formatItems);
1200
+ return rootSlot;
1201
+ }
1202
+ readComponent(el, slot, formatItems) {
1203
+ if (el.nodeType === Node.ELEMENT_NODE) {
1204
+ if (el.tagName === 'BR') {
1205
+ slot.insert('\n');
1206
+ return;
1207
+ }
1208
+ for (const t of this.componentLoaders) {
1209
+ if (t.match(el)) {
1210
+ const result = t.read(el, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
1211
+ return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
1212
+ });
1213
+ if (result instanceof core.Slot) {
1214
+ result.toDelta().forEach(i => slot.insert(i.insert, i.formats));
1215
+ return;
1216
+ }
1217
+ slot.insert(result);
1218
+ return;
1219
+ }
1220
+ }
1221
+ this.readFormats(el, slot, formatItems);
1222
+ }
1223
+ else if (el.nodeType === Node.TEXT_NODE) {
1224
+ const textContent = el.textContent;
1225
+ if (/^\s*[\r\n]+\s*$/.test(textContent)) {
1226
+ return;
1227
+ }
1228
+ slot.insert(textContent);
1229
+ }
1230
+ }
1231
+ readFormats(el, slot, formatItems) {
1232
+ const formats = this.formatLoaders.filter(f => {
1233
+ return f.match(el);
1234
+ }).map(f => {
1235
+ return f.read(el);
1236
+ });
1237
+ const startIndex = slot.index;
1238
+ Array.from(el.childNodes).forEach(child => {
1239
+ this.readComponent(child, slot, formatItems);
1240
+ });
1241
+ const endIndex = slot.index;
1242
+ formatItems.unshift(...formats.map(i => {
1243
+ return {
1244
+ formatter: i.formatter,
1245
+ value: i.value,
1246
+ startIndex,
1247
+ endIndex
1248
+ };
1249
+ }));
1250
+ return formatItems;
1251
+ }
1252
+ readSlot(childSlot, slotRootElement, slotContentElement) {
1253
+ this.attributeLoaders.filter(a => {
1254
+ return a.match(slotRootElement);
1255
+ }).forEach(a => {
1256
+ const r = a.read(slotRootElement);
1257
+ childSlot.setAttribute(r.attribute, r.value);
1258
+ });
1259
+ const childFormatItems = this.readFormats(slotContentElement, childSlot, []);
1260
+ this.applyFormats(childSlot, childFormatItems);
1261
+ return childSlot;
1262
+ }
1263
+ applyFormats(slot, formatItems) {
1264
+ formatItems.forEach(i => {
1265
+ slot.retain(i.startIndex);
1266
+ slot.retain(i.endIndex - i.startIndex, i.formatter, i.value);
1267
+ });
1268
+ }
1269
+ };
1270
+ exports.Parser = Parser_1 = __decorate([
1271
+ di.Injectable(),
1272
+ __param(0, di.Inject(EDITOR_OPTIONS)),
1273
+ __metadata("design:paramtypes", [Object, di.Injector])
1274
+ ], exports.Parser);
1275
+
1276
+ const iframeHTML = `
1277
+ <!DOCTYPE html>
1278
+ <html>
1279
+ <head>
1280
+ <meta charset="UTF-8">
1281
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
1282
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
1283
+ <title>Textbus</title>
1284
+ <style>
1285
+ html {position: fixed; left:0; overflow: hidden}
1286
+ html, body{height: 100%;width:100%}
1287
+ body{margin:0; overflow: hidden}
1288
+ textarea{width: 2000px;height: 100%;opacity: 0; padding: 0; outline: none; border: none; position: absolute; left:0; top:0;}
1289
+ </style>
1290
+ </head>
1291
+ <body>
1292
+ </body>
1293
+ </html>
1294
+ `;
1295
+ class ExperimentalCaret {
1296
+ constructor(scheduler, editorMask) {
1297
+ this.scheduler = scheduler;
1298
+ this.editorMask = editorMask;
1299
+ this.timer = null;
1300
+ this.oldPosition = null;
1301
+ this._display = true;
1302
+ this.flashing = true;
1303
+ this.subs = [];
1304
+ this.positionChangeEvent = new stream.Subject();
1305
+ this.styleChangeEvent = new stream.Subject();
1306
+ this.oldRange = null;
1307
+ this.onPositionChange = this.positionChangeEvent.pipe(stream.distinctUntilChanged());
1308
+ this.onStyleChange = this.styleChangeEvent.asObservable();
1309
+ this.elementRef = createElement('div', {
1310
+ styles: {
1311
+ position: 'absolute',
1312
+ width: '2px',
1313
+ pointerEvents: 'none'
1314
+ },
1315
+ children: [
1316
+ this.caret = createElement('span', {
1317
+ styles: {
1318
+ width: '100%',
1319
+ height: '100%',
1320
+ position: 'absolute',
1321
+ left: 0,
1322
+ top: 0
1323
+ }
1324
+ })
1325
+ ]
1326
+ });
1327
+ this.subs.push(stream.fromEvent(document, 'mousedown').subscribe(() => {
1328
+ this.flashing = false;
1329
+ }), stream.fromEvent(document, 'mouseup').subscribe(() => {
1330
+ this.flashing = true;
1331
+ }));
1332
+ this.editorMask.appendChild(this.elementRef);
1333
+ }
1334
+ get rect() {
1335
+ return this.caret.getBoundingClientRect();
1336
+ }
1337
+ set display(v) {
1338
+ this._display = v;
1339
+ this.caret.style.visibility = v ? 'visible' : 'hidden';
1340
+ }
1341
+ get display() {
1342
+ return this._display;
1343
+ }
1344
+ show(range, restart) {
1345
+ const oldRect = this.elementRef.getBoundingClientRect();
1346
+ this.oldPosition = {
1347
+ top: oldRect.top,
1348
+ left: oldRect.left,
1349
+ height: oldRect.height
1350
+ };
1351
+ this.oldRange = range;
1352
+ if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1353
+ clearTimeout(this.timer);
1354
+ }
1355
+ this.updateCursorPosition(range);
1356
+ if (range.collapsed) {
1357
+ if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1358
+ this.display = true;
1359
+ const toggleShowHide = () => {
1360
+ this.display = !this.display || !this.flashing;
1361
+ this.timer = setTimeout(toggleShowHide, 400);
1362
+ };
1363
+ clearTimeout(this.timer);
1364
+ this.timer = setTimeout(toggleShowHide, 400);
1365
+ }
1366
+ }
1367
+ else {
1368
+ this.display = false;
1369
+ clearTimeout(this.timer);
1370
+ }
1371
+ }
1372
+ hide() {
1373
+ this.display = false;
1374
+ clearTimeout(this.timer);
1375
+ this.positionChangeEvent.next(null);
1376
+ }
1377
+ destroy() {
1378
+ clearTimeout(this.timer);
1379
+ this.subs.forEach(i => i.unsubscribe());
1380
+ }
1381
+ correctScrollTop(scroller) {
1382
+ this.subs.forEach(i => i.unsubscribe());
1383
+ this.subs = [];
1384
+ const scheduler = this.scheduler;
1385
+ let docIsChanged = true;
1386
+ function limitPosition(position) {
1387
+ const { top, bottom } = scroller.getLimit();
1388
+ const caretTop = position.top;
1389
+ if (caretTop + position.height > bottom) {
1390
+ const offset = caretTop - bottom + position.height;
1391
+ scroller.setOffset(offset);
1392
+ }
1393
+ else if (position.top < top) {
1394
+ scroller.setOffset(-(top - position.top));
1395
+ }
1396
+ }
1397
+ let isPressed = false;
1398
+ this.subs.push(scroller.onScroll.subscribe(() => {
1399
+ if (this.oldPosition) {
1400
+ const rect = this.rect;
1401
+ this.oldPosition.top = rect.top;
1402
+ this.oldPosition.left = rect.left;
1403
+ this.oldPosition.height = rect.height;
1404
+ }
1405
+ }), stream.fromEvent(document, 'mousedown', true).subscribe(() => {
1406
+ isPressed = true;
1407
+ }), stream.fromEvent(document, 'mouseup', true).subscribe(() => {
1408
+ isPressed = false;
1409
+ }), scheduler.onDocChange.subscribe(() => {
1410
+ docIsChanged = true;
1411
+ }), this.onPositionChange.subscribe(position => {
1412
+ if (position) {
1413
+ if (docIsChanged) {
1414
+ if (scheduler.lastChangesHasLocalUpdate) {
1415
+ limitPosition(position);
1416
+ }
1417
+ else if (this.oldPosition) {
1418
+ const offset = Math.floor(position.top - this.oldPosition.top);
1419
+ scroller.setOffset(offset);
1420
+ }
1421
+ }
1422
+ else if (!isPressed) {
1423
+ if (this.oldPosition) {
1424
+ const offset = Math.floor(position.top - this.oldPosition.top);
1425
+ scroller.setOffset(offset);
1426
+ }
1427
+ else {
1428
+ limitPosition(position);
1429
+ }
1430
+ }
1431
+ }
1432
+ docIsChanged = false;
1433
+ }));
1434
+ }
1435
+ updateCursorPosition(nativeRange) {
1436
+ const startContainer = nativeRange.startContainer;
1437
+ const node = (startContainer.nodeType === Node.ELEMENT_NODE ? startContainer : startContainer.parentNode);
1438
+ if ((node === null || node === void 0 ? void 0 : node.nodeType) !== Node.ELEMENT_NODE || !nativeRange.collapsed) {
1439
+ this.positionChangeEvent.next(null);
1440
+ return;
1441
+ }
1442
+ const rect = getLayoutRectByRange(nativeRange);
1443
+ const { fontSize, lineHeight, color } = getComputedStyle(node);
1444
+ let height;
1445
+ if (isNaN(+lineHeight)) {
1446
+ const f = parseFloat(lineHeight);
1447
+ if (isNaN(f)) {
1448
+ height = parseFloat(fontSize);
1449
+ }
1450
+ else {
1451
+ height = f;
1452
+ }
1453
+ }
1454
+ else {
1455
+ height = parseFloat(fontSize) * parseFloat(lineHeight);
1456
+ }
1457
+ const boxHeight = Math.floor(Math.max(height, rect.height));
1458
+ // const boxHeight = Math.floor(height)
1459
+ let rectTop = rect.top;
1460
+ if (rect.height < height) {
1461
+ rectTop -= (height - rect.height) / 2;
1462
+ }
1463
+ rectTop = Math.floor(rectTop);
1464
+ const containerRect = this.editorMask.getBoundingClientRect();
1465
+ const top = Math.floor(rectTop - containerRect.top);
1466
+ const left = Math.floor(rect.left - containerRect.left);
1467
+ Object.assign(this.elementRef.style, {
1468
+ left: left + 'px',
1469
+ top: top + 'px',
1470
+ height: boxHeight + 'px',
1471
+ lineHeight: boxHeight + 'px',
1472
+ fontSize
1473
+ });
1474
+ this.caret.style.backgroundColor = color;
1475
+ this.styleChangeEvent.next({
1476
+ height: boxHeight + 'px',
1477
+ lineHeight: boxHeight + 'px',
1478
+ fontSize
1479
+ });
1480
+ this.positionChangeEvent.next({
1481
+ left,
1482
+ top: rectTop,
1483
+ height: boxHeight
1484
+ });
1485
+ }
1486
+ }
1487
+ /**
1488
+ * Textbus PC 端输入实现
1489
+ */
1490
+ exports.MagicInput = class MagicInput extends Input {
1491
+ constructor(parser, keyboard, commander, selection, controller, scheduler, injector) {
1492
+ super();
1493
+ this.parser = parser;
1494
+ this.keyboard = keyboard;
1495
+ this.commander = commander;
1496
+ this.selection = selection;
1497
+ this.controller = controller;
1498
+ this.scheduler = scheduler;
1499
+ this.injector = injector;
1500
+ this.composition = false;
1501
+ this.caret = new ExperimentalCaret(this.scheduler, this.injector.get(VIEW_MASK));
1502
+ this.container = this.createEditableFrame();
1503
+ this.subscription = new stream.Subscription();
1504
+ this.textarea = null;
1505
+ this.isFocus = false;
1506
+ this.nativeFocus = false;
1507
+ this.isSafari = isSafari();
1508
+ this.isMac = isMac();
1509
+ this.isWindows = isWindows();
1510
+ this.isSougouPinYin = false; // 有 bug 版本搜狗拼音
1511
+ this.onReady = new Promise(resolve => {
1512
+ this.subscription.add(stream.fromEvent(this.container, 'load').subscribe(() => {
1513
+ const doc = this.container.contentDocument;
1514
+ doc.open();
1515
+ doc.write(iframeHTML);
1516
+ doc.close();
1517
+ this.doc = doc;
1518
+ this.init();
1519
+ resolve();
1520
+ }), controller.onReadonlyStateChange.subscribe(() => {
1521
+ if (controller.readonly) {
1522
+ this.blur();
1523
+ }
1524
+ }));
1525
+ });
1526
+ this.caret.elementRef.append(this.container);
1527
+ }
1528
+ focus(range, restart) {
1529
+ var _a;
1530
+ this.caret.show(range, restart);
1531
+ if (this.controller.readonly) {
1532
+ return;
1533
+ }
1534
+ if (!this.isFocus) {
1535
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1536
+ setTimeout(() => {
1537
+ var _a, _b, _c;
1538
+ if (!this.nativeFocus && this.isFocus) {
1539
+ this.subscription.unsubscribe();
1540
+ (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1541
+ this.subscription = new stream.Subscription();
1542
+ this.init();
1543
+ (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1544
+ }
1545
+ });
1546
+ }
1547
+ this.isFocus = true;
1548
+ }
1549
+ blur() {
1550
+ var _a;
1551
+ this.caret.hide();
1552
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.blur();
1553
+ this.isFocus = false;
1554
+ }
1555
+ destroy() {
1556
+ this.caret.destroy();
1557
+ this.subscription.unsubscribe();
1558
+ }
1559
+ init() {
1560
+ const doc = this.doc;
1561
+ const contentBody = doc.body;
1562
+ const textarea = doc.createElement('textarea');
1563
+ contentBody.appendChild(textarea);
1564
+ this.textarea = textarea;
1565
+ this.subscription.add(stream.fromEvent(textarea, 'blur').subscribe(() => {
1566
+ this.isFocus = false;
1567
+ this.nativeFocus = false;
1568
+ this.caret.hide();
1569
+ }), stream.fromEvent(textarea, 'focus').subscribe(() => {
1570
+ this.nativeFocus = true;
1571
+ }), this.caret.onStyleChange.subscribe(style => {
1572
+ Object.assign(textarea.style, style);
1573
+ }));
1574
+ this.handleInput(textarea);
1575
+ this.handleShortcut(textarea);
1576
+ this.handleDefaultActions(textarea);
1577
+ }
1578
+ handleDefaultActions(textarea) {
1579
+ this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
1580
+ const selection = this.selection;
1581
+ if (!selection.isSelected) {
1582
+ return;
1583
+ }
1584
+ if (selection.startSlot === selection.endSlot && selection.endOffset - selection.startOffset === 1) {
1585
+ const content = selection.startSlot.getContentAtIndex(selection.startOffset);
1586
+ if (typeof content === 'object') {
1587
+ const clipboardData = ev.clipboardData;
1588
+ const nativeSelection = document.getSelection();
1589
+ const range = nativeSelection.getRangeAt(0);
1590
+ const div = document.createElement('div');
1591
+ const fragment = range.cloneContents();
1592
+ div.append(fragment);
1593
+ clipboardData.setData('text/html', div.innerHTML);
1594
+ clipboardData.setData('text', div.innerText);
1595
+ ev.preventDefault();
1596
+ }
1597
+ }
1598
+ }), stream.fromEvent(textarea, 'paste').subscribe(ev => {
1599
+ const text = ev.clipboardData.getData('Text');
1600
+ const files = Array.from(ev.clipboardData.files);
1601
+ if (files.length) {
1602
+ Promise.all(files.filter(i => {
1603
+ return /image/i.test(i.type);
1604
+ }).map(item => {
1605
+ const reader = new FileReader();
1606
+ return new Promise(resolve => {
1607
+ reader.onload = (event) => {
1608
+ resolve(event.target.result);
1609
+ };
1610
+ reader.readAsDataURL(item);
1611
+ });
1612
+ })).then(urls => {
1613
+ const html = urls.map(i => {
1614
+ return `<img src=${i}>`;
1615
+ }).join('');
1616
+ this.handlePaste(html, text);
1617
+ });
1618
+ ev.preventDefault();
1619
+ return;
1620
+ }
1621
+ const div = this.doc.createElement('div');
1622
+ div.style.cssText = 'width:1px; height:10px; overflow: hidden; position: fixed; left: 50%; top: 50%; opacity:0';
1623
+ div.contentEditable = 'true';
1624
+ this.doc.body.appendChild(div);
1625
+ div.focus();
1626
+ setTimeout(() => {
1627
+ const html = div.innerHTML;
1628
+ this.handlePaste(html, text);
1629
+ this.doc.body.removeChild(div);
1630
+ });
1631
+ }));
1632
+ }
1633
+ handlePaste(html, text) {
1634
+ const slot = this.parser.parse(html, new core.Slot([
1635
+ core.ContentType.BlockComponent,
1636
+ core.ContentType.InlineComponent,
1637
+ core.ContentType.Text
1638
+ ]));
1639
+ this.commander.paste(slot, text);
1640
+ }
1641
+ handleShortcut(textarea) {
1642
+ let isWriting = false;
1643
+ let isIgnore = false;
1644
+ this.subscription.add(stream.fromEvent(textarea, 'compositionstart').subscribe(() => {
1645
+ isWriting = true;
1646
+ }), stream.fromEvent(textarea, 'compositionend').subscribe(() => {
1647
+ isWriting = false;
1648
+ }), stream.fromEvent(textarea, 'beforeinput').subscribe(ev => {
1649
+ if (this.isSafari) {
1650
+ if (ev.inputType === 'insertFromComposition') {
1651
+ isIgnore = true;
1652
+ }
1653
+ }
1654
+ }), stream.fromEvent(textarea, 'keydown').pipe(stream.filter(() => {
1655
+ if (this.isSafari && isIgnore) {
1656
+ isIgnore = false;
1657
+ return false;
1658
+ }
1659
+ return !isWriting; // || !this.textarea.value
1660
+ })).subscribe(ev => {
1661
+ let key = ev.key;
1662
+ const b = key === 'Process' && ev.code === 'Digit2';
1663
+ if (b) {
1664
+ key = '@';
1665
+ this.isSougouPinYin = true;
1666
+ ev.preventDefault();
1667
+ }
1668
+ const is = this.keyboard.execShortcut({
1669
+ key: key,
1670
+ altKey: ev.altKey,
1671
+ shiftKey: ev.shiftKey,
1672
+ ctrlKey: this.isMac ? ev.metaKey : ev.ctrlKey
1673
+ });
1674
+ if (is) {
1675
+ ev.preventDefault();
1676
+ }
1677
+ }));
1678
+ }
1679
+ handleInput(textarea) {
1680
+ this.subscription.add(stream.merge(stream.fromEvent(textarea, 'beforeinput').pipe(stream.filter(ev => {
1681
+ ev.preventDefault();
1682
+ if (this.isSafari) {
1683
+ return ev.inputType === 'insertText' || ev.inputType === 'insertFromComposition';
1684
+ }
1685
+ return !ev.isComposing && !!ev.data;
1686
+ }), stream.map(ev => {
1687
+ return ev.data;
1688
+ })), this.isSafari ? new stream.Observable() : stream.fromEvent(textarea, 'compositionend').pipe(stream.map(ev => {
1689
+ ev.preventDefault();
1690
+ textarea.value = '';
1691
+ return ev.data;
1692
+ }), stream.filter(() => {
1693
+ const b = this.isSougouPinYin;
1694
+ this.isSougouPinYin = false;
1695
+ return !b;
1696
+ }))).subscribe(text => {
1697
+ if (text) {
1698
+ this.commander.write(text);
1699
+ }
1700
+ }));
1701
+ }
1702
+ createEditableFrame() {
1703
+ return createElement('iframe', {
1704
+ attrs: {
1705
+ scrolling: 'no'
1706
+ },
1707
+ styles: {
1708
+ border: 'none',
1709
+ width: '100%',
1710
+ display: 'block',
1711
+ height: '100%',
1712
+ position: 'relative',
1713
+ top: this.isWindows ? '6px' : '0'
1714
+ }
1715
+ });
1716
+ }
1717
+ };
1718
+ exports.MagicInput = __decorate([
1719
+ di.Injectable(),
1720
+ __metadata("design:paramtypes", [exports.Parser,
1721
+ core.Keyboard,
1722
+ core.Commander,
1723
+ core.Selection,
1724
+ core.Controller,
1725
+ core.Scheduler,
1726
+ di.Injector])
1727
+ ], exports.MagicInput);
1728
+
1729
+ class NativeCaret {
1730
+ constructor(scheduler) {
1731
+ this.scheduler = scheduler;
1732
+ this.onPositionChange = new stream.Subject();
1733
+ this.oldPosition = null;
1734
+ this._nativeRange = null;
1735
+ this.subs = [];
1719
1736
  }
1720
- insertBefore(newNode, ref) {
1721
- ref.parentNode.insertBefore(newNode, ref);
1737
+ set nativeRange(range) {
1738
+ this._nativeRange = range;
1739
+ if (range && range.collapsed) {
1740
+ this.onPositionChange.next(range.getBoundingClientRect());
1741
+ }
1722
1742
  }
1723
- getChildByIndex(parent, index) {
1724
- return parent.childNodes[index] || null;
1743
+ get nativeRange() {
1744
+ return this._nativeRange;
1725
1745
  }
1726
- addClass(target, name) {
1727
- target.classList.add(name);
1746
+ get rect() {
1747
+ if (this.nativeRange) {
1748
+ return this.nativeRange.getBoundingClientRect();
1749
+ }
1750
+ return {
1751
+ left: 0,
1752
+ top: 0,
1753
+ width: 0,
1754
+ height: 0
1755
+ };
1728
1756
  }
1729
- removeClass(target, name) {
1730
- target.classList.remove(name);
1757
+ correctScrollTop(scroller) {
1758
+ this.destroy();
1759
+ const scheduler = this.scheduler;
1760
+ let docIsChanged = true;
1761
+ function limitPosition(position) {
1762
+ const { top, bottom } = scroller.getLimit();
1763
+ const caretTop = position.top;
1764
+ if (caretTop + position.height > bottom) {
1765
+ const offset = caretTop - bottom + position.height;
1766
+ scroller.setOffset(offset);
1767
+ }
1768
+ else if (position.top < top) {
1769
+ scroller.setOffset(-(top - position.top));
1770
+ }
1771
+ }
1772
+ let isPressed = false;
1773
+ this.subs.push(scroller.onScroll.subscribe(() => {
1774
+ if (this.oldPosition) {
1775
+ const rect = this.rect;
1776
+ this.oldPosition.top = rect.top;
1777
+ this.oldPosition.left = rect.left;
1778
+ this.oldPosition.height = rect.height;
1779
+ }
1780
+ }), stream.fromEvent(document, 'mousedown', true).subscribe(() => {
1781
+ isPressed = true;
1782
+ }), stream.fromEvent(document, 'mouseup', true).subscribe(() => {
1783
+ isPressed = false;
1784
+ }), scheduler.onDocChange.subscribe(() => {
1785
+ docIsChanged = true;
1786
+ }), this.onPositionChange.subscribe(position => {
1787
+ if (position) {
1788
+ if (docIsChanged) {
1789
+ if (scheduler.lastChangesHasLocalUpdate) {
1790
+ limitPosition(position);
1791
+ }
1792
+ else if (this.oldPosition) {
1793
+ const offset = Math.floor(position.top - this.oldPosition.top);
1794
+ scroller.setOffset(offset);
1795
+ }
1796
+ }
1797
+ else if (!isPressed) {
1798
+ if (this.oldPosition) {
1799
+ const offset = Math.floor(position.top - this.oldPosition.top);
1800
+ scroller.setOffset(offset);
1801
+ }
1802
+ else {
1803
+ limitPosition(position);
1804
+ }
1805
+ }
1806
+ }
1807
+ docIsChanged = false;
1808
+ }));
1731
1809
  }
1732
- setStyle(target, key, value) {
1733
- target.style[key] = value !== null && value !== void 0 ? value : '';
1810
+ destroy() {
1811
+ this.subs.forEach(i => i.unsubscribe());
1812
+ this.subs = [];
1734
1813
  }
1735
- removeStyle(target, key) {
1736
- target.style[key] = '';
1814
+ }
1815
+ exports.NativeInput = class NativeInput extends Input {
1816
+ constructor(injector, parser, scheduler, selection, keyboard, commander, controller) {
1817
+ super();
1818
+ this.injector = injector;
1819
+ this.parser = parser;
1820
+ this.scheduler = scheduler;
1821
+ this.selection = selection;
1822
+ this.keyboard = keyboard;
1823
+ this.commander = commander;
1824
+ this.controller = controller;
1825
+ this.caret = new NativeCaret(this.scheduler);
1826
+ this.composition = false;
1827
+ this.onReady = Promise.resolve();
1828
+ this.nativeSelection = document.getSelection();
1829
+ this.subscription = new stream.Subscription();
1830
+ this.nativeRange = null;
1831
+ this.isSafari = isSafari();
1832
+ this.isMac = isMac();
1833
+ this.isSougouPinYin = false; // 有 bug 版本搜狗拼音
1834
+ this.documentView = injector.get(VIEW_DOCUMENT);
1835
+ if (!controller.readonly) {
1836
+ this.documentView.contentEditable = 'true';
1837
+ }
1838
+ this.subscription.add(controller.onReadonlyStateChange.subscribe(() => {
1839
+ this.documentView.contentEditable = controller.readonly ? 'false' : 'true';
1840
+ }));
1841
+ this.handleShortcut(this.documentView);
1842
+ this.handleInput(this.documentView);
1843
+ this.handleDefaultActions(this.documentView);
1737
1844
  }
1738
- setAttribute(target, key, value) {
1739
- if (this.possibleXlinkNames[key]) {
1740
- this.setXlinkAttribute(target, this.possibleXlinkNames[key], value);
1845
+ focus(nativeRange) {
1846
+ if (this.controller.readonly) {
1741
1847
  return;
1742
1848
  }
1743
- target.setAttribute(key, value);
1744
- const propNames = this.formElement[target.tagName.toLowerCase()];
1745
- if (propNames && propNames.includes(key)) {
1746
- target[key] = Boolean(value);
1747
- }
1849
+ this.caret.nativeRange = nativeRange;
1850
+ this.nativeRange = nativeRange;
1748
1851
  }
1749
- removeAttribute(target, key) {
1750
- if (this.possibleXlinkNames[key]) {
1751
- this.removeXlinkAttribute(target, this.possibleXlinkNames[key]);
1752
- }
1753
- target.removeAttribute(key);
1754
- const propNames = this.formElement[target.tagName.toLowerCase()];
1755
- if (propNames && propNames.includes(key)) {
1756
- target[key] = false;
1852
+ blur() {
1853
+ if (this.nativeRange && this.nativeSelection.rangeCount > 0) {
1854
+ const current = this.nativeSelection.getRangeAt(0);
1855
+ if (current === this.nativeRange) {
1856
+ this.nativeSelection.removeAllRanges();
1857
+ this.nativeRange = null;
1858
+ return;
1859
+ }
1757
1860
  }
1758
1861
  }
1759
- setXlinkAttribute(target, key, value) {
1760
- target.setAttributeNS(this.xlinkNameSpace, key, value);
1862
+ destroy() {
1863
+ this.caret.destroy();
1864
+ this.subscription.unsubscribe();
1761
1865
  }
1762
- removeXlinkAttribute(target, key) {
1763
- target.removeAttributeNS(this.xlinkNameSpace, key);
1866
+ handleDefaultActions(textarea) {
1867
+ this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
1868
+ const selection = this.selection;
1869
+ if (!selection.isSelected) {
1870
+ return;
1871
+ }
1872
+ if (selection.startSlot === selection.endSlot && selection.endOffset - selection.startOffset === 1) {
1873
+ const content = selection.startSlot.getContentAtIndex(selection.startOffset);
1874
+ if (typeof content === 'object') {
1875
+ const clipboardData = ev.clipboardData;
1876
+ const nativeSelection = document.getSelection();
1877
+ const range = nativeSelection.getRangeAt(0);
1878
+ const div = document.createElement('div');
1879
+ const fragment = range.cloneContents();
1880
+ div.append(fragment);
1881
+ clipboardData.setData('text/html', div.innerHTML);
1882
+ clipboardData.setData('text', div.innerText);
1883
+ ev.preventDefault();
1884
+ }
1885
+ }
1886
+ }), stream.fromEvent(textarea, 'paste').subscribe(ev => {
1887
+ const text = ev.clipboardData.getData('Text');
1888
+ const files = Array.from(ev.clipboardData.files);
1889
+ if (files.length) {
1890
+ Promise.all(files.filter(i => {
1891
+ return /image/i.test(i.type);
1892
+ }).map(item => {
1893
+ const reader = new FileReader();
1894
+ return new Promise(resolve => {
1895
+ reader.onload = (event) => {
1896
+ resolve(event.target.result);
1897
+ };
1898
+ reader.readAsDataURL(item);
1899
+ });
1900
+ })).then(urls => {
1901
+ const html = urls.map(i => {
1902
+ return `<img src=${i}>`;
1903
+ }).join('');
1904
+ this.handlePaste(html, text);
1905
+ });
1906
+ ev.preventDefault();
1907
+ return;
1908
+ }
1909
+ const div = document.createElement('div');
1910
+ div.style.cssText = 'width:1px; height:10px; overflow: hidden; position: fixed; left: 50%; top: 50%; opacity:0';
1911
+ div.contentEditable = 'true';
1912
+ document.body.appendChild(div);
1913
+ div.focus();
1914
+ setTimeout(() => {
1915
+ const html = div.innerHTML;
1916
+ this.handlePaste(html, text);
1917
+ document.body.removeChild(div);
1918
+ });
1919
+ }));
1764
1920
  }
1765
- replace(newChild, oldChild) {
1766
- oldChild.parentNode.replaceChild(newChild, oldChild);
1921
+ handlePaste(html, text) {
1922
+ const slot = this.parser.parse(html, new core.Slot([
1923
+ core.ContentType.BlockComponent,
1924
+ core.ContentType.InlineComponent,
1925
+ core.ContentType.Text
1926
+ ]));
1927
+ this.commander.paste(slot, text);
1767
1928
  }
1768
- copy() {
1769
- document.execCommand('copy');
1929
+ handleShortcut(input) {
1930
+ let isWriting = false;
1931
+ let isIgnore = false;
1932
+ this.subscription.add(stream.fromEvent(input, 'compositionstart').subscribe(() => {
1933
+ isWriting = true;
1934
+ }), stream.fromEvent(input, 'compositionend').subscribe(() => {
1935
+ isWriting = false;
1936
+ }), stream.fromEvent(input, 'beforeinput').subscribe(ev => {
1937
+ if (this.isSafari) {
1938
+ if (ev.inputType === 'insertFromComposition') {
1939
+ isIgnore = true;
1940
+ }
1941
+ }
1942
+ }), stream.fromEvent(input, 'keydown').pipe(stream.filter(() => {
1943
+ if (this.isSafari && isIgnore) {
1944
+ isIgnore = false;
1945
+ return false;
1946
+ }
1947
+ return !isWriting; // || !this.textarea.value
1948
+ })).subscribe(ev => {
1949
+ let key = ev.key;
1950
+ const b = key === 'Process' && ev.code === 'Digit2';
1951
+ if (b) {
1952
+ key = '@';
1953
+ this.isSougouPinYin = true;
1954
+ ev.preventDefault();
1955
+ }
1956
+ const is = this.keyboard.execShortcut({
1957
+ key: key,
1958
+ altKey: ev.altKey,
1959
+ shiftKey: ev.shiftKey,
1960
+ ctrlKey: this.isMac ? ev.metaKey : ev.ctrlKey
1961
+ });
1962
+ if (is) {
1963
+ ev.preventDefault();
1964
+ }
1965
+ }));
1770
1966
  }
1771
- static replaceEmpty(s, target) {
1772
- return s.replace(/\s\s+/g, str => {
1773
- return ' ' + Array.from({
1774
- length: str.length - 1
1775
- }).fill(target).join('');
1776
- }).replace(/^\s|\s$/g, target);
1967
+ handleInput(input) {
1968
+ this.subscription.add(stream.fromEvent(input, 'compositionstart').subscribe(() => {
1969
+ this.composition = true;
1970
+ }), stream.merge(stream.fromEvent(input, 'beforeinput').pipe(stream.filter(ev => {
1971
+ ev.preventDefault();
1972
+ if (this.isSafari) {
1973
+ return ev.inputType === 'insertText' || ev.inputType === 'insertFromComposition';
1974
+ }
1975
+ return !ev.isComposing && !!ev.data;
1976
+ }), stream.map(ev => {
1977
+ return ev.data;
1978
+ })), this.isSafari ? new stream.Observable() : stream.fromEvent(input, 'compositionend').pipe(stream.map(ev => {
1979
+ ev.preventDefault();
1980
+ return ev.data;
1981
+ }), stream.filter(() => {
1982
+ const b = this.isSougouPinYin;
1983
+ this.isSougouPinYin = false;
1984
+ return !b;
1985
+ }))).subscribe(text => {
1986
+ this.composition = false;
1987
+ if (text) {
1988
+ this.commander.write(text);
1989
+ }
1990
+ }));
1777
1991
  }
1778
1992
  };
1779
- exports.DomRenderer = DomRenderer_1 = __decorate([
1780
- di.Injectable()
1781
- ], exports.DomRenderer);
1993
+ exports.NativeInput = __decorate([
1994
+ di.Injectable(),
1995
+ __metadata("design:paramtypes", [di.Injector,
1996
+ exports.Parser,
1997
+ core.Scheduler,
1998
+ core.Selection,
1999
+ core.Keyboard,
2000
+ core.Commander,
2001
+ core.Controller])
2002
+ ], exports.NativeInput);
1782
2003
 
1783
2004
  var OutputTranslator_1;
1784
2005
  /**
@@ -2040,6 +2261,9 @@ class Viewer extends core.Starter {
2040
2261
  }, {
2041
2262
  provide: core.NativeSelectionBridge,
2042
2263
  useClass: exports.SelectionBridge
2264
+ }, {
2265
+ provide: Input,
2266
+ useClass: options.useContentEditable ? exports.NativeInput : exports.MagicInput
2043
2267
  }, {
2044
2268
  provide: Viewer,
2045
2269
  useFactory: () => this
@@ -2049,8 +2273,6 @@ class Viewer extends core.Starter {
2049
2273
  ...staticProviders,
2050
2274
  exports.DomRenderer,
2051
2275
  exports.Parser,
2052
- exports.Input,
2053
- exports.Caret,
2054
2276
  exports.SelectionBridge,
2055
2277
  exports.OutputTranslator,
2056
2278
  exports.CollaborateCursor
@@ -2134,10 +2356,10 @@ class Viewer extends core.Starter {
2134
2356
  host.appendChild(this.workbench);
2135
2357
  yield _super.mount.call(this, doc, component);
2136
2358
  const renderer = this.get(core.Renderer);
2137
- const caret = this.get(exports.Caret);
2359
+ const input = this.get(Input);
2138
2360
  this.subs.push(renderer.onViewUpdated.subscribe(() => {
2139
2361
  this.changeEvent.next();
2140
- }), caret.onPositionChange.pipe(stream.map(p => !!p), stream.distinctUntilChanged()).subscribe(b => {
2362
+ }), input.caret.onPositionChange.pipe(stream.map(p => !!p), stream.distinctUntilChanged()).subscribe(b => {
2141
2363
  if (b) {
2142
2364
  this._isFocus = true;
2143
2365
  this.focusEvent.next();
@@ -2147,10 +2369,9 @@ class Viewer extends core.Starter {
2147
2369
  this.blurEvent.next();
2148
2370
  }
2149
2371
  }));
2150
- this.get(exports.Input);
2151
2372
  this.isReady = true;
2152
2373
  if (this.options.autoFocus) {
2153
- this.get(exports.Input).onReady.then(() => {
2374
+ input.onReady.then(() => {
2154
2375
  this.focus();
2155
2376
  });
2156
2377
  }
@@ -2223,7 +2444,7 @@ class Viewer extends core.Starter {
2223
2444
  this.destroyed = true;
2224
2445
  this.subs.forEach(i => i.unsubscribe());
2225
2446
  const types = [
2226
- exports.Input,
2447
+ Input
2227
2448
  ];
2228
2449
  types.forEach(i => {
2229
2450
  this.get(i).destroy();
@@ -2314,7 +2535,8 @@ class Viewer extends core.Starter {
2314
2535
  wordBreak: 'break-all',
2315
2536
  boxSizing: 'border-box',
2316
2537
  minHeight,
2317
- flex: 1
2538
+ flex: 1,
2539
+ outline: 'none'
2318
2540
  },
2319
2541
  attrs: {
2320
2542
  'data-textbus-view': VIEW_DOCUMENT,
@@ -2367,6 +2589,7 @@ class Viewer extends core.Starter {
2367
2589
  exports.CollaborateSelectionAwarenessDelegate = CollaborateSelectionAwarenessDelegate;
2368
2590
  exports.DefaultShortcut = DefaultShortcut;
2369
2591
  exports.EDITOR_OPTIONS = EDITOR_OPTIONS;
2592
+ exports.Input = Input;
2370
2593
  exports.VIEW_CONTAINER = VIEW_CONTAINER;
2371
2594
  exports.VIEW_DOCUMENT = VIEW_DOCUMENT;
2372
2595
  exports.VIEW_MASK = VIEW_MASK;