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