@textbus/platform-browser 3.0.0-alpha.25 → 3.0.0-alpha.27

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,1685 +159,1856 @@ 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);
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);
320
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
794
  }
860
- disConnect() {
861
- this.connector = null;
862
- this.unListen();
795
+ refresh() {
796
+ this.draw(this.currentSelection);
863
797
  }
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);
798
+ destroy() {
799
+ this.subscription.unsubscribe();
878
800
  }
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
919
- }
920
- 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;
970
- }
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
- }
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;
1121
898
  }
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;
899
+ for (let i = rects.length; i < this.tooltips.children.length; i++) {
900
+ this.tooltips.removeChild(this.tooltips.children[i]);
1160
901
  }
1161
- connector.setSelection(null);
1162
902
  }
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);
903
+ getUserCursor(index) {
904
+ let child = this.tooltips.children[index];
905
+ if (child) {
906
+ const anchor = child.children[0];
1200
907
  return {
1201
- node: nativeNode.parentNode,
1202
- offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
908
+ cursor: child,
909
+ anchor,
910
+ userTip: anchor.children[0]
1203
911
  };
1204
912
  }
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);
1274
- }
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);
913
+ const userTip = createElement('span', {
914
+ styles: {
915
+ position: 'absolute',
916
+ left: '50%',
917
+ transform: 'translateX(-50%)',
918
+ marginBottom: '2px',
919
+ bottom: '100%',
920
+ whiteSpace: 'nowrap',
921
+ color: '#fff',
922
+ boxShadow: '0 1px 2px rgba(0,0,0,.1)',
923
+ opacity: 0.8,
924
+ borderRadius: '3px',
925
+ padding: '3px 5px',
926
+ pointerEvents: 'none',
1286
927
  }
1287
- return this.findFocusNodeByParent(node, toAfter, excludeNodes);
1288
- }
1289
- return null;
928
+ });
929
+ const anchor = createElement('span', {
930
+ styles: {
931
+ position: 'absolute',
932
+ top: '-2px',
933
+ left: '-2px',
934
+ width: '5px',
935
+ height: '5px',
936
+ borderRadius: '50%',
937
+ pointerEvents: 'auto',
938
+ pointer: 'cursor',
939
+ },
940
+ children: [userTip]
941
+ });
942
+ child = createElement('span', {
943
+ styles: {
944
+ position: 'absolute',
945
+ },
946
+ children: [
947
+ anchor
948
+ ]
949
+ });
950
+ this.tooltips.append(child);
951
+ return {
952
+ cursor: child,
953
+ anchor,
954
+ userTip
955
+ };
1290
956
  }
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) {
1302
- return {
1303
- slot: position.slot,
1304
- offset: toAfter ? position.startIndex : position.endIndex
1305
- };
1306
- }
1307
- const firstChild = toAfter ? node.firstChild : node.lastChild;
1308
- if (firstChild) {
1309
- return this.findFocusNode(firstChild, toAfter, excludeNodes);
957
+ };
958
+ CollaborateCursor = __decorate([
959
+ Injectable(),
960
+ __param(4, Optional()),
961
+ __metadata("design:paramtypes", [Injector,
962
+ SelectionBridge,
963
+ Scheduler,
964
+ Selection,
965
+ CollaborateSelectionAwarenessDelegate])
966
+ ], CollaborateCursor);
967
+
968
+ var DomRenderer_1;
969
+ /**
970
+ * Textbus PC 端浏览器渲染能力实现
971
+ */
972
+ let DomRenderer = DomRenderer_1 = class DomRenderer {
973
+ constructor() {
974
+ this.isSVG = new RegExp(`^(${[
975
+ // 'a',
976
+ 'animate',
977
+ 'animateMotion',
978
+ 'animateTransform',
979
+ 'circle',
980
+ 'clipPath',
981
+ 'defs',
982
+ 'desc',
983
+ 'ellipse',
984
+ 'feBlend',
985
+ 'feColorMatrix',
986
+ 'feComponentTransfer',
987
+ 'feComposite',
988
+ 'feConvolveMatrix',
989
+ 'feDiffuseLighting',
990
+ 'feDisplacementMap',
991
+ 'feDistantLight',
992
+ 'feDropShadow',
993
+ 'feFlood',
994
+ 'feFuncA',
995
+ 'feFuncB',
996
+ 'feFuncG',
997
+ 'feFuncR',
998
+ 'feGaussianBlur',
999
+ 'feImage',
1000
+ 'feMerge',
1001
+ 'feMergeNode',
1002
+ 'feMorphology',
1003
+ 'feOffset',
1004
+ 'fePointLight',
1005
+ 'feSpecularLighting',
1006
+ 'feSpotLight',
1007
+ 'feTile',
1008
+ 'feTurbulence',
1009
+ 'filter',
1010
+ 'foreignObject',
1011
+ 'g',
1012
+ 'image',
1013
+ 'line',
1014
+ 'linearGradient',
1015
+ 'marker',
1016
+ 'mask',
1017
+ 'metadata',
1018
+ 'mpath',
1019
+ 'path',
1020
+ 'pattern',
1021
+ 'polygon',
1022
+ 'polyline',
1023
+ 'radialGradient',
1024
+ 'rect',
1025
+ // 'script',
1026
+ 'set',
1027
+ 'stop',
1028
+ // 'style',
1029
+ 'svg',
1030
+ 'switch',
1031
+ 'symbol',
1032
+ 'text',
1033
+ 'textPath',
1034
+ 'title',
1035
+ 'tspan',
1036
+ 'use',
1037
+ 'view'
1038
+ ].join('|')})$`, 'i');
1039
+ this.xlinkNameSpace = 'http://www.w3.org/1999/xlink';
1040
+ this.possibleXlinkNames = {
1041
+ xlinkActuate: 'xlink:actuate',
1042
+ xlinkactuate: 'xlink:actuate',
1043
+ 'xlink:actuate': 'xlink:actuate',
1044
+ xlinkArcrole: 'xlink:arcrole',
1045
+ xlinkarcrole: 'xlink:arcrole',
1046
+ 'xlink:arcrole': 'xlink:arcrole',
1047
+ xlinkHref: 'xlink:href',
1048
+ xlinkhref: 'xlink:href',
1049
+ 'xlink:href': 'xlink:href',
1050
+ xlinkRole: 'xlink:role',
1051
+ xlinkrole: 'xlink:role',
1052
+ 'xlink:role': 'xlink:role',
1053
+ xlinkShow: 'xlink:show',
1054
+ xlinkshow: 'xlink:show',
1055
+ 'xlink:show': 'xlink:show',
1056
+ xlinkTitle: 'xlink:title',
1057
+ xlinktitle: 'xlink:title',
1058
+ 'xlink:title': 'xlink:title',
1059
+ xlinkType: 'xlink:type',
1060
+ xlinktype: 'xlink:type',
1061
+ 'xlink:type': 'xlink:type'
1062
+ };
1063
+ this.formElement = {
1064
+ input: ['disabled', 'readonly', 'value'],
1065
+ select: ['disabled', 'readonly'],
1066
+ option: ['disabled', 'selected', 'value'],
1067
+ button: ['disabled'],
1068
+ video: ['controls', 'autoplay', 'loop', 'muted'],
1069
+ audio: ['controls', 'autoplay', 'loop', 'muted'],
1070
+ };
1071
+ }
1072
+ listen(node, type, callback) {
1073
+ node.addEventListener(type, callback);
1074
+ }
1075
+ unListen(node, type, callback) {
1076
+ node.removeEventListener(type, callback);
1077
+ }
1078
+ createTextNode(textContent) {
1079
+ return document.createTextNode(DomRenderer_1.replaceEmpty(textContent, '\u00a0'));
1080
+ }
1081
+ createElement(name) {
1082
+ if (this.isSVG.test(name)) {
1083
+ return document.createElementNS('http://www.w3.org/2000/svg', name);
1310
1084
  }
1311
- const nextSibling = toAfter ? node.nextSibling : node.previousSibling;
1312
- if (nextSibling) {
1313
- return this.findFocusNode(nextSibling, toAfter, excludeNodes);
1085
+ return document.createElement(name);
1086
+ }
1087
+ appendChild(parent, newChild) {
1088
+ parent.appendChild(newChild);
1089
+ }
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;
1314
1112
  }
1315
- return this.findFocusNodeByParent(node, toAfter, excludeNodes);
1316
1113
  }
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);
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;
1329
1121
  }
1330
- return null;
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);
1331
1156
  }
1332
1157
  };
1333
- SelectionBridge = __decorate([
1334
- Injectable(),
1335
- __metadata("design:paramtypes", [Injector,
1336
- Caret,
1337
- Controller,
1338
- Selection,
1339
- RootComponentRef,
1340
- Input,
1341
- Renderer])
1342
- ], SelectionBridge);
1158
+ DomRenderer = DomRenderer_1 = __decorate([
1159
+ Injectable()
1160
+ ], DomRenderer);
1343
1161
 
1344
- class CollaborateSelectionAwarenessDelegate {
1345
- }
1346
- let CollaborateCursor = class CollaborateCursor {
1347
- constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
1162
+ var Parser_1;
1163
+ let Parser = Parser_1 = class Parser {
1164
+ constructor(options, injector) {
1165
+ var _a;
1166
+ this.options = options;
1348
1167
  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
- }
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 || []));
1363
1180
  });
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
- }));
1181
+ this.componentLoaders = componentLoaders;
1182
+ this.formatLoaders = formatLoaders;
1183
+ this.attributeLoaders = attributeLoaders;
1419
1184
  }
1420
- refresh() {
1421
- this.draw(this.currentSelection);
1185
+ static parseHTML(html) {
1186
+ return new DOMParser().parseFromString(html, 'text/html').body;
1422
1187
  }
1423
- destroy() {
1424
- this.subscription.unsubscribe();
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
+ });
1425
1193
  }
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) {
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');
1453
1204
  return;
1454
1205
  }
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
- });
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
+ }
1486
1218
  }
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) {
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)) {
1502
1224
  return;
1503
1225
  }
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]);
1226
+ slot.insert(textContent);
1526
1227
  }
1527
1228
  }
1528
- getUserCursor(index) {
1529
- let child = this.tooltips.children[index];
1530
- if (child) {
1531
- const anchor = child.children[0];
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 => {
1532
1241
  return {
1533
- cursor: child,
1534
- anchor,
1535
- userTip: anchor.children[0]
1242
+ formatter: i.formatter,
1243
+ value: i.value,
1244
+ startIndex,
1245
+ endIndex
1536
1246
  };
1537
- }
1538
- const userTip = createElement('span', {
1539
- styles: {
1540
- position: 'absolute',
1541
- left: '50%',
1542
- transform: 'translateX(-50%)',
1543
- marginBottom: '2px',
1544
- bottom: '100%',
1545
- whiteSpace: 'nowrap',
1546
- color: '#fff',
1547
- boxShadow: '0 1px 2px rgba(0,0,0,.1)',
1548
- opacity: 0.8,
1549
- borderRadius: '3px',
1550
- padding: '3px 5px',
1551
- pointerEvents: 'none',
1552
- }
1553
- });
1554
- const anchor = createElement('span', {
1555
- styles: {
1556
- position: 'absolute',
1557
- top: '-2px',
1558
- left: '-2px',
1559
- width: '5px',
1560
- height: '5px',
1561
- borderRadius: '50%',
1562
- pointerEvents: 'auto',
1563
- pointer: 'cursor',
1564
- },
1565
- children: [userTip]
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);
1566
1256
  });
1567
- child = createElement('span', {
1568
- styles: {
1569
- position: 'absolute',
1570
- },
1571
- children: [
1572
- anchor
1573
- ]
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);
1574
1265
  });
1575
- this.tooltips.append(child);
1576
- return {
1577
- cursor: child,
1578
- anchor,
1579
- userTip
1580
- };
1581
1266
  }
1582
1267
  };
1583
- CollaborateCursor = __decorate([
1268
+ Parser = Parser_1 = __decorate([
1584
1269
  Injectable(),
1585
- __param(4, Optional()),
1586
- __metadata("design:paramtypes", [Injector,
1587
- SelectionBridge,
1588
- Scheduler,
1589
- Selection,
1590
- CollaborateSelectionAwarenessDelegate])
1591
- ], CollaborateCursor);
1270
+ __param(0, Inject(EDITOR_OPTIONS)),
1271
+ __metadata("design:paramtypes", [Object, Injector])
1272
+ ], Parser);
1592
1273
 
1593
- var DomRenderer_1;
1594
- /**
1595
- * Textbus PC 端浏览器渲染能力实现
1596
- */
1597
- let DomRenderer = DomRenderer_1 = class DomRenderer {
1598
- constructor() {
1599
- this.isSVG = new RegExp(`^(${[
1600
- // 'a',
1601
- 'animate',
1602
- 'animateMotion',
1603
- 'animateTransform',
1604
- 'circle',
1605
- 'clipPath',
1606
- 'defs',
1607
- 'desc',
1608
- 'ellipse',
1609
- 'feBlend',
1610
- 'feColorMatrix',
1611
- 'feComponentTransfer',
1612
- 'feComposite',
1613
- 'feConvolveMatrix',
1614
- 'feDiffuseLighting',
1615
- 'feDisplacementMap',
1616
- 'feDistantLight',
1617
- 'feDropShadow',
1618
- 'feFlood',
1619
- 'feFuncA',
1620
- 'feFuncB',
1621
- 'feFuncG',
1622
- 'feFuncR',
1623
- 'feGaussianBlur',
1624
- 'feImage',
1625
- 'feMerge',
1626
- 'feMergeNode',
1627
- 'feMorphology',
1628
- 'feOffset',
1629
- 'fePointLight',
1630
- 'feSpecularLighting',
1631
- 'feSpotLight',
1632
- 'feTile',
1633
- 'feTurbulence',
1634
- 'filter',
1635
- 'foreignObject',
1636
- 'g',
1637
- 'image',
1638
- 'line',
1639
- 'linearGradient',
1640
- 'marker',
1641
- 'mask',
1642
- 'metadata',
1643
- 'mpath',
1644
- 'path',
1645
- 'pattern',
1646
- 'polygon',
1647
- 'polyline',
1648
- 'radialGradient',
1649
- 'rect',
1650
- // 'script',
1651
- 'set',
1652
- 'stop',
1653
- // 'style',
1654
- 'svg',
1655
- 'switch',
1656
- 'symbol',
1657
- 'text',
1658
- 'textPath',
1659
- 'title',
1660
- 'tspan',
1661
- 'use',
1662
- 'view'
1663
- ].join('|')})$`, 'i');
1664
- this.xlinkNameSpace = 'http://www.w3.org/1999/xlink';
1665
- this.possibleXlinkNames = {
1666
- xlinkActuate: 'xlink:actuate',
1667
- xlinkactuate: 'xlink:actuate',
1668
- 'xlink:actuate': 'xlink:actuate',
1669
- xlinkArcrole: 'xlink:arcrole',
1670
- xlinkarcrole: 'xlink:arcrole',
1671
- 'xlink:arcrole': 'xlink:arcrole',
1672
- xlinkHref: 'xlink:href',
1673
- xlinkhref: 'xlink:href',
1674
- 'xlink:href': 'xlink:href',
1675
- xlinkRole: 'xlink:role',
1676
- xlinkrole: 'xlink:role',
1677
- 'xlink:role': 'xlink:role',
1678
- xlinkShow: 'xlink:show',
1679
- xlinkshow: 'xlink:show',
1680
- 'xlink:show': 'xlink:show',
1681
- xlinkTitle: 'xlink:title',
1682
- xlinktitle: 'xlink:title',
1683
- 'xlink:title': 'xlink:title',
1684
- xlinkType: 'xlink:type',
1685
- xlinktype: 'xlink:type',
1686
- 'xlink:type': 'xlink:type'
1687
- };
1688
- this.formElement = {
1689
- input: ['disabled', 'readonly', 'value'],
1690
- select: ['disabled', 'readonly'],
1691
- option: ['disabled', 'selected', 'value'],
1692
- button: ['disabled'],
1693
- video: ['controls', 'autoplay', 'loop', 'muted'],
1694
- audio: ['controls', 'autoplay', 'loop', 'muted'],
1695
- };
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.isFixed = false;
1306
+ this.onPositionChange = this.positionChangeEvent.pipe(distinctUntilChanged());
1307
+ this.onStyleChange = this.styleChangeEvent.asObservable();
1308
+ this.elementRef = createElement('div', {
1309
+ styles: {
1310
+ position: 'absolute',
1311
+ width: '2px',
1312
+ pointerEvents: 'none'
1313
+ },
1314
+ children: [
1315
+ this.caret = createElement('span', {
1316
+ styles: {
1317
+ width: '100%',
1318
+ height: '100%',
1319
+ position: 'absolute',
1320
+ left: 0,
1321
+ top: 0
1322
+ }
1323
+ })
1324
+ ]
1325
+ });
1326
+ this.subs.push(fromEvent(document, 'mousedown').subscribe(() => {
1327
+ this.flashing = false;
1328
+ }), fromEvent(document, 'mouseup').subscribe(() => {
1329
+ this.flashing = true;
1330
+ }));
1331
+ this.editorMask.appendChild(this.elementRef);
1696
1332
  }
1697
- listen(node, type, callback) {
1698
- node.addEventListener(type, callback);
1333
+ get rect() {
1334
+ return this.caret.getBoundingClientRect();
1699
1335
  }
1700
- unListen(node, type, callback) {
1701
- node.removeEventListener(type, callback);
1336
+ set display(v) {
1337
+ this._display = v;
1338
+ this.caret.style.visibility = v ? 'visible' : 'hidden';
1702
1339
  }
1703
- createTextNode(textContent) {
1704
- return document.createTextNode(DomRenderer_1.replaceEmpty(textContent, '\u00a0'));
1340
+ get display() {
1341
+ return this._display;
1705
1342
  }
1706
- createElement(name) {
1707
- if (this.isSVG.test(name)) {
1708
- return document.createElementNS('http://www.w3.org/2000/svg', name);
1343
+ refresh(isFixedCaret = false) {
1344
+ this.isFixed = isFixedCaret;
1345
+ if (this.oldRange) {
1346
+ this.show(this.oldRange, false);
1709
1347
  }
1710
- return document.createElement(name);
1348
+ this.isFixed = false;
1349
+ }
1350
+ show(range, restart) {
1351
+ const oldRect = this.elementRef.getBoundingClientRect();
1352
+ this.oldPosition = {
1353
+ top: oldRect.top,
1354
+ left: oldRect.left,
1355
+ height: oldRect.height
1356
+ };
1357
+ this.oldRange = range;
1358
+ if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1359
+ clearTimeout(this.timer);
1360
+ }
1361
+ this.updateCursorPosition(range);
1362
+ if (range.collapsed) {
1363
+ if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1364
+ this.display = true;
1365
+ const toggleShowHide = () => {
1366
+ this.display = !this.display || !this.flashing;
1367
+ this.timer = setTimeout(toggleShowHide, 400);
1368
+ };
1369
+ clearTimeout(this.timer);
1370
+ this.timer = setTimeout(toggleShowHide, 400);
1371
+ }
1372
+ }
1373
+ else {
1374
+ this.display = false;
1375
+ clearTimeout(this.timer);
1376
+ }
1377
+ }
1378
+ hide() {
1379
+ this.display = false;
1380
+ clearTimeout(this.timer);
1381
+ this.positionChangeEvent.next(null);
1382
+ }
1383
+ destroy() {
1384
+ clearTimeout(this.timer);
1385
+ this.subs.forEach(i => i.unsubscribe());
1386
+ }
1387
+ correctScrollTop(scroller) {
1388
+ this.subs.forEach(i => i.unsubscribe());
1389
+ this.subs = [];
1390
+ const scheduler = this.scheduler;
1391
+ let docIsChanged = true;
1392
+ function limitPosition(position) {
1393
+ const { top, bottom } = scroller.getLimit();
1394
+ const caretTop = position.top;
1395
+ if (caretTop + position.height > bottom) {
1396
+ const offset = caretTop - bottom + position.height;
1397
+ scroller.setOffset(offset);
1398
+ }
1399
+ else if (position.top < top) {
1400
+ scroller.setOffset(-(top - position.top));
1401
+ }
1402
+ }
1403
+ let isPressed = false;
1404
+ this.subs.push(scroller.onScroll.subscribe(() => {
1405
+ if (this.oldPosition) {
1406
+ const rect = this.rect;
1407
+ this.oldPosition.top = rect.top;
1408
+ this.oldPosition.left = rect.left;
1409
+ this.oldPosition.height = rect.height;
1410
+ }
1411
+ }), fromEvent(document, 'mousedown', true).subscribe(() => {
1412
+ isPressed = true;
1413
+ }), fromEvent(document, 'mouseup', true).subscribe(() => {
1414
+ isPressed = false;
1415
+ }), scheduler.onDocChange.subscribe(() => {
1416
+ docIsChanged = true;
1417
+ }), this.onPositionChange.subscribe(position => {
1418
+ if (position) {
1419
+ if (docIsChanged) {
1420
+ if (scheduler.lastChangesHasLocalUpdate) {
1421
+ limitPosition(position);
1422
+ }
1423
+ else if (this.oldPosition) {
1424
+ const offset = Math.floor(position.top - this.oldPosition.top);
1425
+ scroller.setOffset(offset);
1426
+ }
1427
+ }
1428
+ else if (!isPressed) {
1429
+ if (this.isFixed && this.oldPosition) {
1430
+ const offset = Math.floor(position.top - this.oldPosition.top);
1431
+ scroller.setOffset(offset);
1432
+ }
1433
+ else {
1434
+ limitPosition(position);
1435
+ }
1436
+ }
1437
+ }
1438
+ docIsChanged = false;
1439
+ }));
1440
+ }
1441
+ updateCursorPosition(nativeRange) {
1442
+ const startContainer = nativeRange.startContainer;
1443
+ const node = (startContainer.nodeType === Node.ELEMENT_NODE ? startContainer : startContainer.parentNode);
1444
+ if ((node === null || node === void 0 ? void 0 : node.nodeType) !== Node.ELEMENT_NODE || !nativeRange.collapsed) {
1445
+ this.positionChangeEvent.next(null);
1446
+ return;
1447
+ }
1448
+ const rect = getLayoutRectByRange(nativeRange);
1449
+ const { fontSize, lineHeight, color } = getComputedStyle(node);
1450
+ let height;
1451
+ if (isNaN(+lineHeight)) {
1452
+ const f = parseFloat(lineHeight);
1453
+ if (isNaN(f)) {
1454
+ height = parseFloat(fontSize);
1455
+ }
1456
+ else {
1457
+ height = f;
1458
+ }
1459
+ }
1460
+ else {
1461
+ height = parseFloat(fontSize) * parseFloat(lineHeight);
1462
+ }
1463
+ const boxHeight = Math.floor(Math.max(height, rect.height));
1464
+ // const boxHeight = Math.floor(height)
1465
+ let rectTop = rect.top;
1466
+ if (rect.height < height) {
1467
+ rectTop -= (height - rect.height) / 2;
1468
+ }
1469
+ rectTop = Math.floor(rectTop);
1470
+ const containerRect = this.editorMask.getBoundingClientRect();
1471
+ const top = Math.floor(rectTop - containerRect.top);
1472
+ const left = Math.floor(rect.left - containerRect.left);
1473
+ Object.assign(this.elementRef.style, {
1474
+ left: left + 'px',
1475
+ top: top + 'px',
1476
+ height: boxHeight + 'px',
1477
+ lineHeight: boxHeight + 'px',
1478
+ fontSize
1479
+ });
1480
+ this.caret.style.backgroundColor = color;
1481
+ this.styleChangeEvent.next({
1482
+ height: boxHeight + 'px',
1483
+ lineHeight: boxHeight + 'px',
1484
+ fontSize
1485
+ });
1486
+ this.positionChangeEvent.next({
1487
+ left,
1488
+ top: rectTop,
1489
+ height: boxHeight
1490
+ });
1491
+ }
1492
+ }
1493
+ /**
1494
+ * Textbus PC 端输入实现
1495
+ */
1496
+ let MagicInput = class MagicInput extends Input {
1497
+ constructor(parser, keyboard, commander, selection, controller, scheduler, injector) {
1498
+ super();
1499
+ this.parser = parser;
1500
+ this.keyboard = keyboard;
1501
+ this.commander = commander;
1502
+ this.selection = selection;
1503
+ this.controller = controller;
1504
+ this.scheduler = scheduler;
1505
+ this.injector = injector;
1506
+ this.composition = false;
1507
+ this.caret = new ExperimentalCaret(this.scheduler, this.injector.get(VIEW_MASK));
1508
+ this.container = this.createEditableFrame();
1509
+ this.subscription = new Subscription();
1510
+ this.textarea = null;
1511
+ this.isFocus = false;
1512
+ this.nativeFocus = false;
1513
+ this.isSafari = isSafari();
1514
+ this.isMac = isMac();
1515
+ this.isWindows = isWindows();
1516
+ this.isSougouPinYin = false; // 有 bug 版本搜狗拼音
1517
+ this.onReady = new Promise(resolve => {
1518
+ this.subscription.add(fromEvent(this.container, 'load').subscribe(() => {
1519
+ const doc = this.container.contentDocument;
1520
+ doc.open();
1521
+ doc.write(iframeHTML);
1522
+ doc.close();
1523
+ this.doc = doc;
1524
+ this.init();
1525
+ resolve();
1526
+ }), controller.onReadonlyStateChange.subscribe(() => {
1527
+ if (controller.readonly) {
1528
+ this.blur();
1529
+ }
1530
+ }));
1531
+ });
1532
+ this.caret.elementRef.append(this.container);
1533
+ }
1534
+ focus(range, restart) {
1535
+ var _a;
1536
+ this.caret.show(range, restart);
1537
+ if (this.controller.readonly) {
1538
+ return;
1539
+ }
1540
+ if (!this.isFocus) {
1541
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1542
+ setTimeout(() => {
1543
+ var _a, _b, _c;
1544
+ if (!this.nativeFocus && this.isFocus) {
1545
+ this.subscription.unsubscribe();
1546
+ (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1547
+ this.subscription = new Subscription();
1548
+ this.init();
1549
+ (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1550
+ }
1551
+ });
1552
+ }
1553
+ this.isFocus = true;
1554
+ }
1555
+ blur() {
1556
+ var _a;
1557
+ this.caret.hide();
1558
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.blur();
1559
+ this.isFocus = false;
1560
+ }
1561
+ destroy() {
1562
+ this.caret.destroy();
1563
+ this.subscription.unsubscribe();
1564
+ }
1565
+ init() {
1566
+ const doc = this.doc;
1567
+ const contentBody = doc.body;
1568
+ const textarea = doc.createElement('textarea');
1569
+ contentBody.appendChild(textarea);
1570
+ this.textarea = textarea;
1571
+ this.subscription.add(fromEvent(textarea, 'blur').subscribe(() => {
1572
+ this.isFocus = false;
1573
+ this.nativeFocus = false;
1574
+ this.caret.hide();
1575
+ }), fromEvent(textarea, 'focus').subscribe(() => {
1576
+ this.nativeFocus = true;
1577
+ }), this.caret.onStyleChange.subscribe(style => {
1578
+ Object.assign(textarea.style, style);
1579
+ }));
1580
+ this.handleInput(textarea);
1581
+ this.handleShortcut(textarea);
1582
+ this.handleDefaultActions(textarea);
1583
+ }
1584
+ handleDefaultActions(textarea) {
1585
+ this.subscription.add(fromEvent(document, 'copy').subscribe(ev => {
1586
+ const selection = this.selection;
1587
+ if (!selection.isSelected) {
1588
+ return;
1589
+ }
1590
+ if (selection.startSlot === selection.endSlot && selection.endOffset - selection.startOffset === 1) {
1591
+ const content = selection.startSlot.getContentAtIndex(selection.startOffset);
1592
+ if (typeof content === 'object') {
1593
+ const clipboardData = ev.clipboardData;
1594
+ const nativeSelection = document.getSelection();
1595
+ const range = nativeSelection.getRangeAt(0);
1596
+ const div = document.createElement('div');
1597
+ const fragment = range.cloneContents();
1598
+ div.append(fragment);
1599
+ clipboardData.setData('text/html', div.innerHTML);
1600
+ clipboardData.setData('text', div.innerText);
1601
+ ev.preventDefault();
1602
+ }
1603
+ }
1604
+ }), fromEvent(textarea, 'paste').subscribe(ev => {
1605
+ const text = ev.clipboardData.getData('Text');
1606
+ const files = Array.from(ev.clipboardData.files);
1607
+ if (files.length) {
1608
+ Promise.all(files.filter(i => {
1609
+ return /image/i.test(i.type);
1610
+ }).map(item => {
1611
+ const reader = new FileReader();
1612
+ return new Promise(resolve => {
1613
+ reader.onload = (event) => {
1614
+ resolve(event.target.result);
1615
+ };
1616
+ reader.readAsDataURL(item);
1617
+ });
1618
+ })).then(urls => {
1619
+ const html = urls.map(i => {
1620
+ return `<img src=${i}>`;
1621
+ }).join('');
1622
+ this.handlePaste(html, text);
1623
+ });
1624
+ ev.preventDefault();
1625
+ return;
1626
+ }
1627
+ const div = this.doc.createElement('div');
1628
+ div.style.cssText = 'width:1px; height:10px; overflow: hidden; position: fixed; left: 50%; top: 50%; opacity:0';
1629
+ div.contentEditable = 'true';
1630
+ this.doc.body.appendChild(div);
1631
+ div.focus();
1632
+ setTimeout(() => {
1633
+ const html = div.innerHTML;
1634
+ this.handlePaste(html, text);
1635
+ this.doc.body.removeChild(div);
1636
+ });
1637
+ }));
1638
+ }
1639
+ handlePaste(html, text) {
1640
+ const slot = this.parser.parse(html, new Slot([
1641
+ ContentType.BlockComponent,
1642
+ ContentType.InlineComponent,
1643
+ ContentType.Text
1644
+ ]));
1645
+ this.commander.paste(slot, text);
1646
+ }
1647
+ handleShortcut(textarea) {
1648
+ let isWriting = false;
1649
+ let isIgnore = false;
1650
+ this.subscription.add(fromEvent(textarea, 'compositionstart').subscribe(() => {
1651
+ isWriting = true;
1652
+ }), fromEvent(textarea, 'compositionend').subscribe(() => {
1653
+ isWriting = false;
1654
+ }), fromEvent(textarea, 'beforeinput').subscribe(ev => {
1655
+ if (this.isSafari) {
1656
+ if (ev.inputType === 'insertFromComposition') {
1657
+ isIgnore = true;
1658
+ }
1659
+ }
1660
+ }), fromEvent(textarea, 'keydown').pipe(filter(() => {
1661
+ if (this.isSafari && isIgnore) {
1662
+ isIgnore = false;
1663
+ return false;
1664
+ }
1665
+ return !isWriting; // || !this.textarea.value
1666
+ })).subscribe(ev => {
1667
+ let key = ev.key;
1668
+ const b = key === 'Process' && ev.code === 'Digit2';
1669
+ if (b) {
1670
+ key = '@';
1671
+ this.isSougouPinYin = true;
1672
+ ev.preventDefault();
1673
+ }
1674
+ const is = this.keyboard.execShortcut({
1675
+ key: key,
1676
+ altKey: ev.altKey,
1677
+ shiftKey: ev.shiftKey,
1678
+ ctrlKey: this.isMac ? ev.metaKey : ev.ctrlKey
1679
+ });
1680
+ if (is) {
1681
+ ev.preventDefault();
1682
+ }
1683
+ }));
1684
+ }
1685
+ handleInput(textarea) {
1686
+ this.subscription.add(merge(fromEvent(textarea, 'beforeinput').pipe(filter(ev => {
1687
+ ev.preventDefault();
1688
+ if (this.isSafari) {
1689
+ return ev.inputType === 'insertText' || ev.inputType === 'insertFromComposition';
1690
+ }
1691
+ return !ev.isComposing && !!ev.data;
1692
+ }), map(ev => {
1693
+ return ev.data;
1694
+ })), this.isSafari ? new Observable() : fromEvent(textarea, 'compositionend').pipe(map(ev => {
1695
+ ev.preventDefault();
1696
+ textarea.value = '';
1697
+ return ev.data;
1698
+ }), filter(() => {
1699
+ const b = this.isSougouPinYin;
1700
+ this.isSougouPinYin = false;
1701
+ return !b;
1702
+ }))).subscribe(text => {
1703
+ if (text) {
1704
+ this.commander.write(text);
1705
+ }
1706
+ }));
1707
+ }
1708
+ createEditableFrame() {
1709
+ return createElement('iframe', {
1710
+ attrs: {
1711
+ scrolling: 'no'
1712
+ },
1713
+ styles: {
1714
+ border: 'none',
1715
+ width: '100%',
1716
+ display: 'block',
1717
+ height: '100%',
1718
+ position: 'relative',
1719
+ top: this.isWindows ? '6px' : '0'
1720
+ }
1721
+ });
1711
1722
  }
1712
- appendChild(parent, newChild) {
1713
- parent.appendChild(newChild);
1723
+ };
1724
+ MagicInput = __decorate([
1725
+ Injectable(),
1726
+ __metadata("design:paramtypes", [Parser,
1727
+ Keyboard,
1728
+ Commander,
1729
+ Selection,
1730
+ Controller,
1731
+ Scheduler,
1732
+ Injector])
1733
+ ], MagicInput);
1734
+
1735
+ class NativeCaret {
1736
+ constructor(scheduler) {
1737
+ this.scheduler = scheduler;
1738
+ this.onPositionChange = new Subject();
1739
+ this.oldPosition = null;
1740
+ this._nativeRange = null;
1741
+ this.subs = [];
1714
1742
  }
1715
- remove(node) {
1716
- node.parentNode.removeChild(node);
1743
+ set nativeRange(range) {
1744
+ this._nativeRange = range;
1745
+ if (range && range.collapsed) {
1746
+ this.onPositionChange.next(range.getBoundingClientRect());
1747
+ }
1717
1748
  }
1718
- insertBefore(newNode, ref) {
1719
- ref.parentNode.insertBefore(newNode, ref);
1749
+ get nativeRange() {
1750
+ return this._nativeRange;
1720
1751
  }
1721
- getChildByIndex(parent, index) {
1722
- return parent.childNodes[index] || null;
1752
+ get rect() {
1753
+ if (this.nativeRange) {
1754
+ return this.nativeRange.getBoundingClientRect();
1755
+ }
1756
+ return {
1757
+ left: 0,
1758
+ top: 0,
1759
+ width: 0,
1760
+ height: 0
1761
+ };
1723
1762
  }
1724
- addClass(target, name) {
1725
- target.classList.add(name);
1763
+ refresh() {
1764
+ //
1726
1765
  }
1727
- removeClass(target, name) {
1728
- target.classList.remove(name);
1766
+ correctScrollTop(scroller) {
1767
+ this.destroy();
1768
+ const scheduler = this.scheduler;
1769
+ let docIsChanged = true;
1770
+ function limitPosition(position) {
1771
+ const { top, bottom } = scroller.getLimit();
1772
+ const caretTop = position.top;
1773
+ if (caretTop + position.height > bottom) {
1774
+ const offset = caretTop - bottom + position.height;
1775
+ scroller.setOffset(offset);
1776
+ }
1777
+ else if (position.top < top) {
1778
+ scroller.setOffset(-(top - position.top));
1779
+ }
1780
+ }
1781
+ let isPressed = false;
1782
+ this.subs.push(scroller.onScroll.subscribe(() => {
1783
+ if (this.oldPosition) {
1784
+ const rect = this.rect;
1785
+ this.oldPosition.top = rect.top;
1786
+ this.oldPosition.left = rect.left;
1787
+ this.oldPosition.height = rect.height;
1788
+ }
1789
+ }), fromEvent(document, 'mousedown', true).subscribe(() => {
1790
+ isPressed = true;
1791
+ }), fromEvent(document, 'mouseup', true).subscribe(() => {
1792
+ isPressed = false;
1793
+ }), scheduler.onDocChange.subscribe(() => {
1794
+ docIsChanged = true;
1795
+ }), this.onPositionChange.subscribe(position => {
1796
+ if (position) {
1797
+ if (docIsChanged) {
1798
+ if (scheduler.lastChangesHasLocalUpdate) {
1799
+ limitPosition(position);
1800
+ }
1801
+ else if (this.oldPosition) {
1802
+ const offset = Math.floor(position.top - this.oldPosition.top);
1803
+ scroller.setOffset(offset);
1804
+ }
1805
+ }
1806
+ else if (!isPressed) {
1807
+ if (this.oldPosition) {
1808
+ const offset = Math.floor(position.top - this.oldPosition.top);
1809
+ scroller.setOffset(offset);
1810
+ }
1811
+ else {
1812
+ limitPosition(position);
1813
+ }
1814
+ }
1815
+ }
1816
+ docIsChanged = false;
1817
+ }));
1729
1818
  }
1730
- setStyle(target, key, value) {
1731
- target.style[key] = value !== null && value !== void 0 ? value : '';
1819
+ destroy() {
1820
+ this.subs.forEach(i => i.unsubscribe());
1821
+ this.subs = [];
1732
1822
  }
1733
- removeStyle(target, key) {
1734
- target.style[key] = '';
1823
+ }
1824
+ let NativeInput = class NativeInput extends Input {
1825
+ constructor(injector, parser, scheduler, selection, keyboard, commander, controller) {
1826
+ super();
1827
+ this.injector = injector;
1828
+ this.parser = parser;
1829
+ this.scheduler = scheduler;
1830
+ this.selection = selection;
1831
+ this.keyboard = keyboard;
1832
+ this.commander = commander;
1833
+ this.controller = controller;
1834
+ this.caret = new NativeCaret(this.scheduler);
1835
+ this.composition = false;
1836
+ this.onReady = Promise.resolve();
1837
+ this.nativeSelection = document.getSelection();
1838
+ this.subscription = new Subscription();
1839
+ this.nativeRange = null;
1840
+ this.isSafari = isSafari();
1841
+ this.isMac = isMac();
1842
+ this.isSougouPinYin = false; // 有 bug 版本搜狗拼音
1843
+ this.documentView = injector.get(VIEW_DOCUMENT);
1844
+ if (!controller.readonly) {
1845
+ this.documentView.contentEditable = 'true';
1846
+ }
1847
+ this.subscription.add(controller.onReadonlyStateChange.subscribe(() => {
1848
+ this.documentView.contentEditable = controller.readonly ? 'false' : 'true';
1849
+ }));
1850
+ this.handleShortcut(this.documentView);
1851
+ this.handleInput(this.documentView);
1852
+ this.handleDefaultActions(this.documentView);
1735
1853
  }
1736
- setAttribute(target, key, value) {
1737
- if (this.possibleXlinkNames[key]) {
1738
- this.setXlinkAttribute(target, this.possibleXlinkNames[key], value);
1854
+ focus(nativeRange) {
1855
+ if (this.controller.readonly) {
1739
1856
  return;
1740
1857
  }
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
- }
1858
+ this.caret.nativeRange = nativeRange;
1859
+ this.nativeRange = nativeRange;
1746
1860
  }
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;
1861
+ blur() {
1862
+ if (this.nativeRange && this.nativeSelection.rangeCount > 0) {
1863
+ const current = this.nativeSelection.getRangeAt(0);
1864
+ if (current === this.nativeRange) {
1865
+ this.nativeSelection.removeAllRanges();
1866
+ this.nativeRange = null;
1867
+ return;
1868
+ }
1755
1869
  }
1756
1870
  }
1757
- setXlinkAttribute(target, key, value) {
1758
- target.setAttributeNS(this.xlinkNameSpace, key, value);
1871
+ destroy() {
1872
+ this.caret.destroy();
1873
+ this.subscription.unsubscribe();
1759
1874
  }
1760
- removeXlinkAttribute(target, key) {
1761
- target.removeAttributeNS(this.xlinkNameSpace, key);
1875
+ handleDefaultActions(textarea) {
1876
+ this.subscription.add(fromEvent(document, 'copy').subscribe(ev => {
1877
+ const selection = this.selection;
1878
+ if (!selection.isSelected) {
1879
+ return;
1880
+ }
1881
+ if (selection.startSlot === selection.endSlot && selection.endOffset - selection.startOffset === 1) {
1882
+ const content = selection.startSlot.getContentAtIndex(selection.startOffset);
1883
+ if (typeof content === 'object') {
1884
+ const clipboardData = ev.clipboardData;
1885
+ const nativeSelection = document.getSelection();
1886
+ const range = nativeSelection.getRangeAt(0);
1887
+ const div = document.createElement('div');
1888
+ const fragment = range.cloneContents();
1889
+ div.append(fragment);
1890
+ clipboardData.setData('text/html', div.innerHTML);
1891
+ clipboardData.setData('text', div.innerText);
1892
+ ev.preventDefault();
1893
+ }
1894
+ }
1895
+ }), fromEvent(textarea, 'paste').subscribe(ev => {
1896
+ const text = ev.clipboardData.getData('Text');
1897
+ const files = Array.from(ev.clipboardData.files);
1898
+ if (files.length) {
1899
+ Promise.all(files.filter(i => {
1900
+ return /image/i.test(i.type);
1901
+ }).map(item => {
1902
+ const reader = new FileReader();
1903
+ return new Promise(resolve => {
1904
+ reader.onload = (event) => {
1905
+ resolve(event.target.result);
1906
+ };
1907
+ reader.readAsDataURL(item);
1908
+ });
1909
+ })).then(urls => {
1910
+ const html = urls.map(i => {
1911
+ return `<img src=${i}>`;
1912
+ }).join('');
1913
+ this.handlePaste(html, text);
1914
+ });
1915
+ ev.preventDefault();
1916
+ return;
1917
+ }
1918
+ const div = document.createElement('div');
1919
+ div.style.cssText = 'width:1px; height:10px; overflow: hidden; position: fixed; left: 50%; top: 50%; opacity:0';
1920
+ div.contentEditable = 'true';
1921
+ document.body.appendChild(div);
1922
+ div.focus();
1923
+ setTimeout(() => {
1924
+ const html = div.innerHTML;
1925
+ this.handlePaste(html, text);
1926
+ document.body.removeChild(div);
1927
+ });
1928
+ }));
1762
1929
  }
1763
- replace(newChild, oldChild) {
1764
- oldChild.parentNode.replaceChild(newChild, oldChild);
1930
+ handlePaste(html, text) {
1931
+ const slot = this.parser.parse(html, new Slot([
1932
+ ContentType.BlockComponent,
1933
+ ContentType.InlineComponent,
1934
+ ContentType.Text
1935
+ ]));
1936
+ this.commander.paste(slot, text);
1765
1937
  }
1766
- copy() {
1767
- document.execCommand('copy');
1938
+ handleShortcut(input) {
1939
+ let isWriting = false;
1940
+ let isIgnore = false;
1941
+ this.subscription.add(fromEvent(input, 'compositionstart').subscribe(() => {
1942
+ isWriting = true;
1943
+ }), fromEvent(input, 'compositionend').subscribe(() => {
1944
+ isWriting = false;
1945
+ }), fromEvent(input, 'beforeinput').subscribe(ev => {
1946
+ if (this.isSafari) {
1947
+ if (ev.inputType === 'insertFromComposition') {
1948
+ isIgnore = true;
1949
+ }
1950
+ }
1951
+ }), fromEvent(input, 'keydown').pipe(filter(() => {
1952
+ if (this.isSafari && isIgnore) {
1953
+ isIgnore = false;
1954
+ return false;
1955
+ }
1956
+ return !isWriting; // || !this.textarea.value
1957
+ })).subscribe(ev => {
1958
+ let key = ev.key;
1959
+ const b = key === 'Process' && ev.code === 'Digit2';
1960
+ if (b) {
1961
+ key = '@';
1962
+ this.isSougouPinYin = true;
1963
+ ev.preventDefault();
1964
+ }
1965
+ const is = this.keyboard.execShortcut({
1966
+ key: key,
1967
+ altKey: ev.altKey,
1968
+ shiftKey: ev.shiftKey,
1969
+ ctrlKey: this.isMac ? ev.metaKey : ev.ctrlKey
1970
+ });
1971
+ if (is) {
1972
+ ev.preventDefault();
1973
+ }
1974
+ }));
1768
1975
  }
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);
1976
+ handleInput(input) {
1977
+ this.subscription.add(fromEvent(input, 'compositionstart').subscribe(() => {
1978
+ this.composition = true;
1979
+ }), merge(fromEvent(input, 'beforeinput').pipe(filter(ev => {
1980
+ ev.preventDefault();
1981
+ if (this.isSafari) {
1982
+ return ev.inputType === 'insertText' || ev.inputType === 'insertFromComposition';
1983
+ }
1984
+ return !ev.isComposing && !!ev.data;
1985
+ }), map(ev => {
1986
+ return ev.data;
1987
+ })), this.isSafari ? new Observable() : fromEvent(input, 'compositionend').pipe(map(ev => {
1988
+ ev.preventDefault();
1989
+ return ev.data;
1990
+ }), filter(() => {
1991
+ const b = this.isSougouPinYin;
1992
+ this.isSougouPinYin = false;
1993
+ return !b;
1994
+ }))).subscribe(text => {
1995
+ this.composition = false;
1996
+ if (text) {
1997
+ this.commander.write(text);
1998
+ }
1999
+ }));
1775
2000
  }
1776
2001
  };
1777
- DomRenderer = DomRenderer_1 = __decorate([
1778
- Injectable()
1779
- ], DomRenderer);
2002
+ NativeInput = __decorate([
2003
+ Injectable(),
2004
+ __metadata("design:paramtypes", [Injector,
2005
+ Parser,
2006
+ Scheduler,
2007
+ Selection,
2008
+ Keyboard,
2009
+ Commander,
2010
+ Controller])
2011
+ ], NativeInput);
1780
2012
 
1781
2013
  var OutputTranslator_1;
1782
2014
  /**
@@ -2038,6 +2270,9 @@ class Viewer extends Starter {
2038
2270
  }, {
2039
2271
  provide: NativeSelectionBridge,
2040
2272
  useClass: SelectionBridge
2273
+ }, {
2274
+ provide: Input,
2275
+ useClass: options.useContentEditable ? NativeInput : MagicInput
2041
2276
  }, {
2042
2277
  provide: Viewer,
2043
2278
  useFactory: () => this
@@ -2047,8 +2282,6 @@ class Viewer extends Starter {
2047
2282
  ...staticProviders,
2048
2283
  DomRenderer,
2049
2284
  Parser,
2050
- Input,
2051
- Caret,
2052
2285
  SelectionBridge,
2053
2286
  OutputTranslator,
2054
2287
  CollaborateCursor
@@ -2132,10 +2365,10 @@ class Viewer extends Starter {
2132
2365
  host.appendChild(this.workbench);
2133
2366
  yield _super.mount.call(this, doc, component);
2134
2367
  const renderer = this.get(Renderer);
2135
- const caret = this.get(Caret);
2368
+ const input = this.get(Input);
2136
2369
  this.subs.push(renderer.onViewUpdated.subscribe(() => {
2137
2370
  this.changeEvent.next();
2138
- }), caret.onPositionChange.pipe(map(p => !!p), distinctUntilChanged()).subscribe(b => {
2371
+ }), input.caret.onPositionChange.pipe(map(p => !!p), distinctUntilChanged()).subscribe(b => {
2139
2372
  if (b) {
2140
2373
  this._isFocus = true;
2141
2374
  this.focusEvent.next();
@@ -2145,10 +2378,9 @@ class Viewer extends Starter {
2145
2378
  this.blurEvent.next();
2146
2379
  }
2147
2380
  }));
2148
- this.get(Input);
2149
2381
  this.isReady = true;
2150
2382
  if (this.options.autoFocus) {
2151
- this.get(Input).onReady.then(() => {
2383
+ input.onReady.then(() => {
2152
2384
  this.focus();
2153
2385
  });
2154
2386
  }
@@ -2221,7 +2453,7 @@ class Viewer extends Starter {
2221
2453
  this.destroyed = true;
2222
2454
  this.subs.forEach(i => i.unsubscribe());
2223
2455
  const types = [
2224
- Input,
2456
+ Input
2225
2457
  ];
2226
2458
  types.forEach(i => {
2227
2459
  this.get(i).destroy();
@@ -2312,7 +2544,8 @@ class Viewer extends Starter {
2312
2544
  wordBreak: 'break-all',
2313
2545
  boxSizing: 'border-box',
2314
2546
  minHeight,
2315
- flex: 1
2547
+ flex: 1,
2548
+ outline: 'none'
2316
2549
  },
2317
2550
  attrs: {
2318
2551
  'data-textbus-view': VIEW_DOCUMENT,
@@ -2362,4 +2595,4 @@ class Viewer extends Starter {
2362
2595
  }
2363
2596
  }
2364
2597
 
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 };
2598
+ 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 };