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