@textbus/platform-browser 3.0.0-alpha.20

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.
@@ -0,0 +1,2378 @@
1
+ 'use strict';
2
+
3
+ require('reflect-metadata');
4
+ var di = require('@tanbo/di');
5
+ var core = require('@textbus/core');
6
+ var stream = require('@tanbo/stream');
7
+
8
+ function createElement(tagName, options = {}) {
9
+ const el = document.createElement(tagName);
10
+ if (options.classes) {
11
+ el.classList.add(...options.classes);
12
+ }
13
+ if (options.attrs) {
14
+ Object.keys(options.attrs).forEach(key => {
15
+ el.setAttribute(key, options.attrs[key]);
16
+ });
17
+ }
18
+ if (options.props) {
19
+ Object.keys(options.props).forEach(key => {
20
+ el[key] = options.props[key];
21
+ });
22
+ }
23
+ if (options.styles) {
24
+ Object.assign(el.style, options.styles);
25
+ }
26
+ if (options.children) {
27
+ options.children.filter(i => i).forEach(item => {
28
+ el.appendChild(item);
29
+ });
30
+ }
31
+ if (options.on) {
32
+ Object.keys(options.on).forEach(key => {
33
+ el.addEventListener(key, options.on[key]);
34
+ });
35
+ }
36
+ return el;
37
+ }
38
+ function createTextNode(content) {
39
+ return document.createTextNode(content);
40
+ }
41
+
42
+ const isWindows = () => /win(dows|32|64)/i.test(navigator.userAgent);
43
+ const isMac = () => /mac os/i.test(navigator.userAgent);
44
+ const isSafari = () => /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
45
+
46
+ /*! *****************************************************************************
47
+ Copyright (c) Microsoft Corporation.
48
+
49
+ Permission to use, copy, modify, and/or distribute this software for any
50
+ purpose with or without fee is hereby granted.
51
+
52
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
53
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
54
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
55
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
56
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
57
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
58
+ PERFORMANCE OF THIS SOFTWARE.
59
+ ***************************************************************************** */
60
+
61
+ function __decorate(decorators, target, key, desc) {
62
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
63
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
64
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
65
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
66
+ }
67
+
68
+ function __param(paramIndex, decorator) {
69
+ return function (target, key) { decorator(target, key, paramIndex); }
70
+ }
71
+
72
+ function __metadata(metadataKey, metadataValue) {
73
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
74
+ }
75
+
76
+ function __awaiter(thisArg, _arguments, P, generator) {
77
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
78
+ return new (P || (P = Promise))(function (resolve, reject) {
79
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
80
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
81
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
82
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
83
+ });
84
+ }
85
+
86
+ /**
87
+ * 编辑器可选项依赖注入 token
88
+ */
89
+ const EDITOR_OPTIONS = new di.InjectionToken('EDITOR_OPTIONS');
90
+ /**
91
+ * 编辑器容器依赖注入 token
92
+ */
93
+ const VIEW_CONTAINER = new di.InjectionToken('VIEW_CONTAINER');
94
+ /**
95
+ * 编辑器容器依赖注入 token
96
+ */
97
+ const VIEW_DOCUMENT = new di.InjectionToken('VIEW_DOCUMENT');
98
+ /**
99
+ * 编辑器容器遮罩层 token
100
+ */
101
+ const VIEW_MASK = new di.InjectionToken('VIEW_MASK');
102
+
103
+ function getLayoutRectByRange(range) {
104
+ let { startContainer, startOffset } = range;
105
+ if (startContainer.nodeType === Node.TEXT_NODE) {
106
+ if (startOffset > 0) {
107
+ return range.getBoundingClientRect();
108
+ }
109
+ const parentNode = startContainer.parentNode;
110
+ startOffset = Array.from(parentNode.childNodes).indexOf(startContainer);
111
+ startContainer = parentNode;
112
+ }
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
+ };
123
+ }
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();
129
+ }
130
+ }
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();
137
+ return {
138
+ left: rect.right,
139
+ top: rect.top,
140
+ width: rect.width,
141
+ height: rect.height
142
+ };
143
+ }
144
+ }
145
+ if (offsetNode) {
146
+ if (offsetNode.nodeType === Node.ELEMENT_NODE && offsetNode.nodeName.toLowerCase() !== 'br') {
147
+ return offsetNode.getBoundingClientRect();
148
+ }
149
+ isInsertBefore = true;
150
+ }
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);
156
+ }
157
+ else {
158
+ startContainer.appendChild(span);
159
+ }
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
+ }
241
+ }
242
+ else {
243
+ this.display = false;
244
+ clearTimeout(this.timer);
245
+ }
246
+ }
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);
265
+ }
266
+ else if (position.top < top) {
267
+ scroller.setOffset(-(top - position.top));
268
+ }
269
+ }
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;
277
+ }
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
+ }
294
+ }
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);
302
+ }
303
+ }
304
+ }
305
+ docIsChanged = false;
306
+ }));
307
+ }
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);
313
+ return;
314
+ }
315
+ const rect = getLayoutRectByRange(nativeRange);
316
+ const { fontSize, lineHeight, color } = getComputedStyle(node);
317
+ let height;
318
+ if (isNaN(+lineHeight)) {
319
+ const f = parseFloat(lineHeight);
320
+ if (isNaN(f)) {
321
+ height = parseFloat(fontSize);
322
+ }
323
+ else {
324
+ height = f;
325
+ }
326
+ }
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;
335
+ }
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;
403
+ }
404
+ readComponent(el, slot, formatItems) {
405
+ if (el.nodeType === Node.ELEMENT_NODE) {
406
+ if (el.tagName === 'BR') {
407
+ slot.insert('\n');
408
+ return;
409
+ }
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;
418
+ }
419
+ slot.insert(result);
420
+ return;
421
+ }
422
+ }
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
+ }
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 => {
445
+ return {
446
+ formatter: i.formatter,
447
+ value: i.value,
448
+ startIndex,
449
+ endIndex
450
+ };
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
+ });
470
+ }
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();
565
+ }
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;
602
+ }
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();
615
+ }
616
+ }
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
+ }
672
+ }
673
+ }), stream.fromEvent(textarea, 'keydown').pipe(stream.filter(() => {
674
+ if (this.isSafari && isIgnore) {
675
+ isIgnore = false;
676
+ return false;
677
+ }
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 = '@';
684
+ }
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();
696
+ }
697
+ }));
698
+ }
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';
704
+ }
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
+ }));
767
+ }
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'
780
+ }
781
+ });
782
+ }
783
+ };
784
+ exports.Input.openExperimentalCompositionInput = false;
785
+ exports.Input = Input_1 = __decorate([
786
+ di.Injectable(),
787
+ __metadata("design:paramtypes", [exports.Parser,
788
+ core.Keyboard,
789
+ core.Commander,
790
+ core.Selection,
791
+ core.Controller,
792
+ core.Scheduler,
793
+ exports.Caret])
794
+ ], exports.Input);
795
+
796
+ /**
797
+ * Textbus PC 端选区桥接实现
798
+ */
799
+ exports.SelectionBridge = class SelectionBridge {
800
+ constructor(injector, caret, controller, selection, rootComponentRef, input, renderer) {
801
+ this.injector = injector;
802
+ this.caret = caret;
803
+ this.controller = controller;
804
+ 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();
827
+ }
828
+ if (r) {
829
+ input.focus();
830
+ }
831
+ else {
832
+ input.blur();
833
+ }
834
+ });
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;
843
+ }
844
+ while (target) {
845
+ if (target.contentEditable === 'true') {
846
+ this.ignoreSelectionChange = true;
847
+ return;
848
+ }
849
+ target = target.parentNode;
850
+ }
851
+ }));
852
+ this.sub.add(stream.fromEvent(document, 'focusout').subscribe(() => {
853
+ this.ignoreSelectionChange = false;
854
+ }));
855
+ }
856
+ connect(connector) {
857
+ this.disConnect();
858
+ this.connector = connector;
859
+ this.syncSelection(connector);
860
+ this.listen(connector);
861
+ }
862
+ disConnect() {
863
+ this.connector = null;
864
+ this.unListen();
865
+ }
866
+ getRect(location) {
867
+ const { focus, anchor } = this.getPositionByRange({
868
+ focusOffset: location.offset,
869
+ anchorOffset: location.offset,
870
+ focusSlot: location.slot,
871
+ anchorSlot: location.slot
872
+ });
873
+ if (!focus || !anchor) {
874
+ return null;
875
+ }
876
+ const nativeRange = document.createRange();
877
+ nativeRange.setStart(focus.node, focus.offset);
878
+ nativeRange.collapse();
879
+ return getLayoutRectByRange(nativeRange);
880
+ }
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;
1006
+ }
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;
1018
+ }
1019
+ if (loopCount > 10000) {
1020
+ break;
1021
+ }
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;
1061
+ }
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;
1073
+ }
1074
+ if (loopCount > 10000) {
1075
+ break;
1076
+ }
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) {
1091
+ return;
1092
+ }
1093
+ if (!ev.shiftKey) {
1094
+ selection.removeAllRanges();
1095
+ }
1096
+ }), stream.fromEvent(document, 'selectionchange').subscribe(() => {
1097
+ this.syncSelection(connector);
1098
+ }));
1099
+ }
1100
+ syncSelection(connector) {
1101
+ var _a;
1102
+ const selection = this.nativeSelection;
1103
+ this.changeFromUser = true;
1104
+ if (this.ignoreSelectionChange ||
1105
+ selection.rangeCount === 0 ||
1106
+ !this.docContainer.contains(selection.anchorNode)) {
1107
+ return;
1108
+ }
1109
+ const nativeRange = selection.getRangeAt(0).cloneRange();
1110
+ const isFocusEnd = selection.focusNode === nativeRange.endContainer && selection.focusOffset === nativeRange.endOffset;
1111
+ const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
1112
+ if (!this.docContainer.contains(selection.focusNode)) {
1113
+ if (isFocusEnd) {
1114
+ const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.first);
1115
+ const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
1116
+ nativeRange.setEndAfter(nativeNode.lastChild);
1117
+ }
1118
+ else {
1119
+ const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.last);
1120
+ const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
1121
+ nativeRange.setStartBefore(nativeNode.firstChild);
1122
+ }
1123
+ }
1124
+ const startPosition = this.getCorrectedPosition(nativeRange.startContainer, nativeRange.startOffset, isFocusStart);
1125
+ const endPosition = nativeRange.collapsed ?
1126
+ startPosition :
1127
+ this.getCorrectedPosition(nativeRange.endContainer, nativeRange.endOffset, isFocusEnd);
1128
+ if ([Node.ELEMENT_NODE, Node.TEXT_NODE].includes((_a = nativeRange.commonAncestorContainer) === null || _a === void 0 ? void 0 : _a.nodeType) &&
1129
+ startPosition && endPosition) {
1130
+ const abstractSelection = isFocusEnd ? {
1131
+ anchorSlot: startPosition.slot,
1132
+ anchorOffset: startPosition.offset,
1133
+ focusSlot: endPosition.slot,
1134
+ focusOffset: endPosition.offset
1135
+ } : {
1136
+ focusSlot: startPosition.slot,
1137
+ focusOffset: startPosition.offset,
1138
+ anchorSlot: endPosition.slot,
1139
+ anchorOffset: endPosition.offset
1140
+ };
1141
+ const { focus, anchor } = this.getPositionByRange(abstractSelection);
1142
+ if (focus && anchor) {
1143
+ let start = anchor;
1144
+ let end = focus;
1145
+ if (isFocusStart) {
1146
+ start = focus;
1147
+ end = anchor;
1148
+ }
1149
+ if (nativeRange.startContainer !== start.node || nativeRange.startOffset !== start.offset) {
1150
+ nativeRange.setStart(start.node, start.offset);
1151
+ }
1152
+ if (nativeRange.endContainer !== end.node || nativeRange.endOffset !== end.offset) {
1153
+ nativeRange.setEnd(end.node, end.offset);
1154
+ }
1155
+ connector.setSelection(abstractSelection);
1156
+ this.selectionChangeEvent.next(nativeRange);
1157
+ }
1158
+ else {
1159
+ connector.setSelection(null);
1160
+ }
1161
+ return;
1162
+ }
1163
+ connector.setSelection(null);
1164
+ }
1165
+ findSelectedNodeAndOffset(slot, offset) {
1166
+ const prev = slot.getContentAtIndex(offset - 1);
1167
+ const vNodes = this.renderer.getVNodesBySlot(slot);
1168
+ if (prev) {
1169
+ if (typeof prev !== 'string') {
1170
+ const vNode = this.renderer.getVNodeByComponent(prev);
1171
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1172
+ return {
1173
+ node: nativeNode.parentNode,
1174
+ offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode) + 1
1175
+ };
1176
+ }
1177
+ else if (prev === '\n') {
1178
+ for (const vNode of vNodes) {
1179
+ if (vNode instanceof core.VTextNode) {
1180
+ continue;
1181
+ }
1182
+ if (vNode.tagName === 'br') {
1183
+ const position = this.renderer.getLocationByVNode(vNode);
1184
+ if (position) {
1185
+ if (position.endIndex === offset) {
1186
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1187
+ const parentNode = nativeNode.parentNode;
1188
+ return {
1189
+ node: parentNode,
1190
+ offset: Array.from(parentNode.childNodes).indexOf(nativeNode) + 1
1191
+ };
1192
+ }
1193
+ }
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ const current = slot.getContentAtIndex(offset);
1199
+ if (current && typeof current !== 'string') {
1200
+ const vNode = this.renderer.getVNodeByComponent(current);
1201
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1202
+ return {
1203
+ node: nativeNode.parentNode,
1204
+ offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
1205
+ };
1206
+ }
1207
+ for (const vNode of vNodes) {
1208
+ if (vNode instanceof core.VElement) {
1209
+ if (vNode.tagName === 'br') {
1210
+ const position = this.renderer.getLocationByVNode(vNode);
1211
+ if (position) {
1212
+ if (position.startIndex === offset) {
1213
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1214
+ const parentNode = nativeNode.parentNode;
1215
+ return {
1216
+ node: parentNode,
1217
+ offset: Array.from(parentNode.childNodes).indexOf(nativeNode)
1218
+ };
1219
+ }
1220
+ }
1221
+ }
1222
+ continue;
1223
+ }
1224
+ const position = this.renderer.getLocationByVNode(vNode);
1225
+ if (position) {
1226
+ if (offset >= position.startIndex && offset <= position.endIndex) {
1227
+ const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
1228
+ return {
1229
+ node: nativeNode,
1230
+ offset: offset - position.startIndex
1231
+ };
1232
+ }
1233
+ }
1234
+ }
1235
+ return null;
1236
+ }
1237
+ getCorrectedPosition(node, offset, toAfter, excludeNodes = []) {
1238
+ excludeNodes.push(node);
1239
+ if (node.nodeType === Node.ELEMENT_NODE) {
1240
+ const containerPosition = this.renderer.getLocationByNativeNode(node);
1241
+ const childNode = node.childNodes[offset];
1242
+ if (childNode) {
1243
+ const childPosition = this.renderer.getLocationByNativeNode(childNode);
1244
+ if (childPosition) {
1245
+ if (containerPosition) {
1246
+ return {
1247
+ slot: childPosition.slot,
1248
+ offset: childPosition.startIndex
1249
+ };
1250
+ }
1251
+ return this.findFocusNode(childNode, toAfter, excludeNodes);
1252
+ }
1253
+ return this.findFocusNode(childNode, toAfter, excludeNodes);
1254
+ }
1255
+ const prevNode = node.childNodes[offset - 1];
1256
+ if (prevNode) {
1257
+ const prevPosition = this.renderer.getLocationByNativeNode(prevNode);
1258
+ if (prevPosition && containerPosition) {
1259
+ return {
1260
+ slot: prevPosition.slot,
1261
+ offset: prevPosition.endIndex
1262
+ };
1263
+ }
1264
+ }
1265
+ if (containerPosition) {
1266
+ return {
1267
+ slot: containerPosition.slot,
1268
+ offset: containerPosition.endIndex
1269
+ };
1270
+ }
1271
+ const nextNode = toAfter ? node.nextSibling : node.previousSibling;
1272
+ if (nextNode) {
1273
+ return this.findFocusNode(nextNode, toAfter, excludeNodes);
1274
+ }
1275
+ return this.findFocusNodeByParent(node, toAfter, excludeNodes);
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);
1288
+ }
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;
1333
+ }
1334
+ };
1335
+ exports.SelectionBridge = __decorate([
1336
+ di.Injectable(),
1337
+ __metadata("design:paramtypes", [di.Injector,
1338
+ exports.Caret,
1339
+ core.Controller,
1340
+ core.Selection,
1341
+ core.RootComponentRef,
1342
+ exports.Input,
1343
+ core.Renderer])
1344
+ ], exports.SelectionBridge);
1345
+
1346
+ class CollaborateSelectionAwarenessDelegate {
1347
+ }
1348
+ exports.CollaborateCursor = class CollaborateCursor {
1349
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate = null) {
1350
+ this.injector = injector;
1351
+ this.nativeSelection = nativeSelection;
1352
+ this.scheduler = scheduler;
1353
+ this.selection = selection;
1354
+ this.awarenessDelegate = awarenessDelegate;
1355
+ this.host = createElement('div', {
1356
+ styles: {
1357
+ position: 'absolute',
1358
+ left: 0,
1359
+ top: 0,
1360
+ width: '100%',
1361
+ height: '100%',
1362
+ pointerEvents: 'none',
1363
+ zIndex: 1
1364
+ }
1365
+ });
1366
+ this.canvasContainer = createElement('div', {
1367
+ styles: {
1368
+ position: 'absolute',
1369
+ left: 0,
1370
+ top: 0,
1371
+ width: '100%',
1372
+ height: '100%',
1373
+ overflow: 'hidden'
1374
+ }
1375
+ });
1376
+ this.canvas = createElement('canvas', {
1377
+ styles: {
1378
+ position: 'absolute',
1379
+ opacity: 0.5,
1380
+ left: 0,
1381
+ top: 0,
1382
+ width: '100%',
1383
+ height: document.documentElement.clientHeight + 'px',
1384
+ pointerEvents: 'none',
1385
+ }
1386
+ });
1387
+ this.context = this.canvas.getContext('2d');
1388
+ this.tooltips = createElement('div', {
1389
+ styles: {
1390
+ position: 'absolute',
1391
+ left: 0,
1392
+ top: 0,
1393
+ width: '100%',
1394
+ height: '100%',
1395
+ pointerEvents: 'none',
1396
+ fontSize: '12px',
1397
+ zIndex: 10
1398
+ }
1399
+ });
1400
+ this.onRectsChange = new stream.Subject();
1401
+ this.subscription = new stream.Subscription();
1402
+ this.currentSelection = [];
1403
+ this.container = injector.get(VIEW_CONTAINER);
1404
+ this.canvasContainer.append(this.canvas);
1405
+ this.host.append(this.canvasContainer, this.tooltips);
1406
+ this.container.prepend(this.host);
1407
+ this.subscription.add(this.onRectsChange.subscribe(rects => {
1408
+ for (const rect of rects) {
1409
+ this.context.fillStyle = rect.color;
1410
+ this.context.beginPath();
1411
+ this.context.rect(rect.left, rect.top, rect.width, rect.height);
1412
+ this.context.fill();
1413
+ this.context.closePath();
1414
+ }
1415
+ }), stream.fromEvent(window, 'resize').subscribe(() => {
1416
+ this.canvas.style.height = document.documentElement.clientHeight + 'px';
1417
+ this.refresh();
1418
+ }), this.scheduler.onDocChanged.subscribe(() => {
1419
+ this.refresh();
1420
+ }));
1421
+ }
1422
+ refresh() {
1423
+ this.draw(this.currentSelection);
1424
+ }
1425
+ destroy() {
1426
+ this.subscription.unsubscribe();
1427
+ }
1428
+ draw(paths) {
1429
+ this.currentSelection = paths;
1430
+ const containerRect = this.container.getBoundingClientRect();
1431
+ this.canvas.style.top = containerRect.top * -1 + 'px';
1432
+ this.canvas.width = this.canvas.offsetWidth;
1433
+ this.canvas.height = this.canvas.offsetHeight;
1434
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1435
+ const users = [];
1436
+ paths.filter(i => {
1437
+ return i.paths.anchor.length && i.paths.focus.length;
1438
+ }).forEach(item => {
1439
+ const anchorPaths = [...item.paths.anchor];
1440
+ const focusPaths = [...item.paths.focus];
1441
+ const anchorOffset = anchorPaths.pop();
1442
+ const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1443
+ const focusOffset = focusPaths.pop();
1444
+ const focusSlot = this.selection.findSlotByPaths(focusPaths);
1445
+ if (!anchorSlot || !focusSlot) {
1446
+ return;
1447
+ }
1448
+ const { focus, anchor } = this.nativeSelection.getPositionByRange({
1449
+ focusOffset,
1450
+ anchorOffset,
1451
+ focusSlot,
1452
+ anchorSlot
1453
+ });
1454
+ if (!focus || !anchor) {
1455
+ return;
1456
+ }
1457
+ const nativeRange = document.createRange();
1458
+ nativeRange.setStart(anchor.node, anchor.offset);
1459
+ nativeRange.setEnd(focus.node, focus.offset);
1460
+ if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1461
+ nativeRange.setStart(focus.node, focus.offset);
1462
+ nativeRange.setEnd(anchor.node, anchor.offset);
1463
+ }
1464
+ let rects = false;
1465
+ if (this.awarenessDelegate) {
1466
+ rects = this.awarenessDelegate.getRects({
1467
+ focusOffset,
1468
+ anchorOffset,
1469
+ focusSlot,
1470
+ anchorSlot
1471
+ }, nativeRange);
1472
+ }
1473
+ if (!rects) {
1474
+ rects = nativeRange.getClientRects();
1475
+ }
1476
+ const selectionRects = [];
1477
+ for (let i = rects.length - 1; i >= 0; i--) {
1478
+ const rect = rects[i];
1479
+ selectionRects.push({
1480
+ id: item.id,
1481
+ color: item.color,
1482
+ username: item.username,
1483
+ left: rect.left - containerRect.left,
1484
+ top: rect.top,
1485
+ width: rect.width,
1486
+ height: rect.height,
1487
+ });
1488
+ }
1489
+ this.onRectsChange.next(selectionRects);
1490
+ const cursorRange = nativeRange.cloneRange();
1491
+ cursorRange.setStart(focus.node, focus.offset);
1492
+ cursorRange.collapse(true);
1493
+ const cursorRect = getLayoutRectByRange(cursorRange);
1494
+ const rect = {
1495
+ id: item.id,
1496
+ username: item.username,
1497
+ color: item.color,
1498
+ left: cursorRect.left - containerRect.left,
1499
+ top: cursorRect.top - containerRect.top,
1500
+ width: 1,
1501
+ height: cursorRect.height
1502
+ };
1503
+ if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
1504
+ return;
1505
+ }
1506
+ users.push(rect);
1507
+ });
1508
+ this.drawUserCursor(users);
1509
+ }
1510
+ drawUserCursor(rects) {
1511
+ for (let i = 0; i < rects.length; i++) {
1512
+ const rect = rects[i];
1513
+ const { cursor, userTip, anchor } = this.getUserCursor(i);
1514
+ Object.assign(cursor.style, {
1515
+ left: rect.left + 'px',
1516
+ top: rect.top + 'px',
1517
+ width: rect.width + 'px',
1518
+ height: rect.height + 'px',
1519
+ background: rect.color,
1520
+ display: 'block'
1521
+ });
1522
+ anchor.style.background = rect.color;
1523
+ userTip.innerText = rect.username;
1524
+ userTip.style.background = rect.color;
1525
+ }
1526
+ for (let i = rects.length; i < this.tooltips.children.length; i++) {
1527
+ this.tooltips.removeChild(this.tooltips.children[i]);
1528
+ }
1529
+ }
1530
+ getUserCursor(index) {
1531
+ let child = this.tooltips.children[index];
1532
+ if (child) {
1533
+ const anchor = child.children[0];
1534
+ return {
1535
+ cursor: child,
1536
+ anchor,
1537
+ userTip: anchor.children[0]
1538
+ };
1539
+ }
1540
+ const userTip = createElement('span', {
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
+ }
1555
+ });
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]
1568
+ });
1569
+ child = createElement('span', {
1570
+ styles: {
1571
+ position: 'absolute',
1572
+ },
1573
+ children: [
1574
+ anchor
1575
+ ]
1576
+ });
1577
+ this.tooltips.append(child);
1578
+ return {
1579
+ cursor: child,
1580
+ anchor,
1581
+ userTip
1582
+ };
1583
+ }
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, Object])
1592
+ ], exports.CollaborateCursor);
1593
+
1594
+ var DomRenderer_1;
1595
+ /**
1596
+ * Textbus PC 端浏览器渲染能力实现
1597
+ */
1598
+ exports.DomRenderer = DomRenderer_1 = class DomRenderer {
1599
+ constructor() {
1600
+ this.isSVG = new RegExp(`^(${[
1601
+ // 'a',
1602
+ 'animate',
1603
+ 'animateMotion',
1604
+ 'animateTransform',
1605
+ 'circle',
1606
+ 'clipPath',
1607
+ 'defs',
1608
+ 'desc',
1609
+ 'ellipse',
1610
+ 'feBlend',
1611
+ 'feColorMatrix',
1612
+ 'feComponentTransfer',
1613
+ 'feComposite',
1614
+ 'feConvolveMatrix',
1615
+ 'feDiffuseLighting',
1616
+ 'feDisplacementMap',
1617
+ 'feDistantLight',
1618
+ 'feDropShadow',
1619
+ 'feFlood',
1620
+ 'feFuncA',
1621
+ 'feFuncB',
1622
+ 'feFuncG',
1623
+ 'feFuncR',
1624
+ 'feGaussianBlur',
1625
+ 'feImage',
1626
+ 'feMerge',
1627
+ 'feMergeNode',
1628
+ 'feMorphology',
1629
+ 'feOffset',
1630
+ 'fePointLight',
1631
+ 'feSpecularLighting',
1632
+ 'feSpotLight',
1633
+ 'feTile',
1634
+ 'feTurbulence',
1635
+ 'filter',
1636
+ 'foreignObject',
1637
+ 'g',
1638
+ 'image',
1639
+ 'line',
1640
+ 'linearGradient',
1641
+ 'marker',
1642
+ 'mask',
1643
+ 'metadata',
1644
+ 'mpath',
1645
+ 'path',
1646
+ 'pattern',
1647
+ 'polygon',
1648
+ 'polyline',
1649
+ 'radialGradient',
1650
+ 'rect',
1651
+ // 'script',
1652
+ 'set',
1653
+ 'stop',
1654
+ // 'style',
1655
+ 'svg',
1656
+ 'switch',
1657
+ 'symbol',
1658
+ 'text',
1659
+ 'textPath',
1660
+ 'title',
1661
+ 'tspan',
1662
+ 'use',
1663
+ 'view'
1664
+ ].join('|')})$`, 'i');
1665
+ this.xlinkNameSpace = 'http://www.w3.org/1999/xlink';
1666
+ this.possibleXlinkNames = {
1667
+ xlinkActuate: 'xlink:actuate',
1668
+ xlinkactuate: 'xlink:actuate',
1669
+ 'xlink:actuate': 'xlink:actuate',
1670
+ xlinkArcrole: 'xlink:arcrole',
1671
+ xlinkarcrole: 'xlink:arcrole',
1672
+ 'xlink:arcrole': 'xlink:arcrole',
1673
+ xlinkHref: 'xlink:href',
1674
+ xlinkhref: 'xlink:href',
1675
+ 'xlink:href': 'xlink:href',
1676
+ xlinkRole: 'xlink:role',
1677
+ xlinkrole: 'xlink:role',
1678
+ 'xlink:role': 'xlink:role',
1679
+ xlinkShow: 'xlink:show',
1680
+ xlinkshow: 'xlink:show',
1681
+ 'xlink:show': 'xlink:show',
1682
+ xlinkTitle: 'xlink:title',
1683
+ xlinktitle: 'xlink:title',
1684
+ 'xlink:title': 'xlink:title',
1685
+ xlinkType: 'xlink:type',
1686
+ xlinktype: 'xlink:type',
1687
+ 'xlink:type': 'xlink:type'
1688
+ };
1689
+ this.formElement = {
1690
+ input: ['disabled', 'readonly', 'value'],
1691
+ select: ['disabled', 'readonly'],
1692
+ option: ['disabled', 'selected', 'value'],
1693
+ button: ['disabled'],
1694
+ video: ['controls', 'autoplay', 'loop', 'muted'],
1695
+ audio: ['controls', 'autoplay', 'loop', 'muted'],
1696
+ };
1697
+ }
1698
+ listen(node, type, callback) {
1699
+ node.addEventListener(type, callback);
1700
+ }
1701
+ unListen(node, type, callback) {
1702
+ node.removeEventListener(type, callback);
1703
+ }
1704
+ createTextNode(textContent) {
1705
+ return document.createTextNode(DomRenderer_1.replaceEmpty(textContent, '\u00a0'));
1706
+ }
1707
+ createElement(name) {
1708
+ if (this.isSVG.test(name)) {
1709
+ return document.createElementNS('http://www.w3.org/2000/svg', name);
1710
+ }
1711
+ return document.createElement(name);
1712
+ }
1713
+ appendChild(parent, newChild) {
1714
+ parent.appendChild(newChild);
1715
+ }
1716
+ remove(node) {
1717
+ node.parentNode.removeChild(node);
1718
+ }
1719
+ insertBefore(newNode, ref) {
1720
+ ref.parentNode.insertBefore(newNode, ref);
1721
+ }
1722
+ getChildByIndex(parent, index) {
1723
+ return parent.childNodes[index] || null;
1724
+ }
1725
+ addClass(target, name) {
1726
+ target.classList.add(name);
1727
+ }
1728
+ removeClass(target, name) {
1729
+ target.classList.remove(name);
1730
+ }
1731
+ setStyle(target, key, value) {
1732
+ target.style[key] = value !== null && value !== void 0 ? value : '';
1733
+ }
1734
+ removeStyle(target, key) {
1735
+ target.style[key] = '';
1736
+ }
1737
+ setAttribute(target, key, value) {
1738
+ if (this.possibleXlinkNames[key]) {
1739
+ this.setXlinkAttribute(target, this.possibleXlinkNames[key], value);
1740
+ return;
1741
+ }
1742
+ target.setAttribute(key, value);
1743
+ const propNames = this.formElement[target.tagName.toLowerCase()];
1744
+ if (propNames && propNames.includes(key)) {
1745
+ target[key] = Boolean(value);
1746
+ }
1747
+ }
1748
+ removeAttribute(target, key) {
1749
+ if (this.possibleXlinkNames[key]) {
1750
+ this.removeXlinkAttribute(target, this.possibleXlinkNames[key]);
1751
+ }
1752
+ target.removeAttribute(key);
1753
+ const propNames = this.formElement[target.tagName.toLowerCase()];
1754
+ if (propNames && propNames.includes(key)) {
1755
+ target[key] = false;
1756
+ }
1757
+ }
1758
+ setXlinkAttribute(target, key, value) {
1759
+ target.setAttributeNS(this.xlinkNameSpace, key, value);
1760
+ }
1761
+ removeXlinkAttribute(target, key) {
1762
+ target.removeAttributeNS(this.xlinkNameSpace, key);
1763
+ }
1764
+ replace(newChild, oldChild) {
1765
+ oldChild.parentNode.replaceChild(newChild, oldChild);
1766
+ }
1767
+ copy() {
1768
+ document.execCommand('copy');
1769
+ }
1770
+ static replaceEmpty(s, target) {
1771
+ return s.replace(/\s\s+/g, str => {
1772
+ return ' ' + Array.from({
1773
+ length: str.length - 1
1774
+ }).fill(target).join('');
1775
+ }).replace(/^\s|\s$/g, target);
1776
+ }
1777
+ };
1778
+ exports.DomRenderer = DomRenderer_1 = __decorate([
1779
+ di.Injectable()
1780
+ ], exports.DomRenderer);
1781
+
1782
+ var OutputTranslator_1;
1783
+ /**
1784
+ * HTML 输出转换器
1785
+ */
1786
+ exports.OutputTranslator = OutputTranslator_1 = class OutputTranslator {
1787
+ constructor() {
1788
+ this.singleTagTest = new RegExp(`^(${OutputTranslator_1.singleTags.join('|')})$`, 'i');
1789
+ }
1790
+ transform(vDom) {
1791
+ return vDom.children.map(child => {
1792
+ return this.vDomToHTMLString(child);
1793
+ }).join('');
1794
+ }
1795
+ vDomToHTMLString(vDom) {
1796
+ const xssFilter = OutputTranslator_1.simpleXSSFilter;
1797
+ if (vDom instanceof core.VTextNode) {
1798
+ return this.replaceEmpty(xssFilter.text(vDom.textContent), '&nbsp;');
1799
+ }
1800
+ const styles = Array.from(vDom.styles.keys()).filter(key => {
1801
+ const v = vDom.styles.get(key);
1802
+ return !(v === undefined || v === null || v === '');
1803
+ }).map(key => {
1804
+ const k = key.replace(/(?=[A-Z])/g, '-').toLowerCase();
1805
+ return xssFilter.attrValue(`${k}:${vDom.styles.get(key)}`);
1806
+ }).join(';');
1807
+ const attrs = Array.from(vDom.attrs.keys()).filter(key => key !== 'ref' && vDom.attrs.get(key) !== false).map(k => {
1808
+ const key = xssFilter.attrName(k);
1809
+ const value = vDom.attrs.get(k);
1810
+ return (value === true ? `${key}` : `${key}="${xssFilter.attrValue(`${value}`)}"`);
1811
+ });
1812
+ if (styles) {
1813
+ attrs.push(`style="${styles}"`);
1814
+ }
1815
+ if (vDom.classes && vDom.classes.size) {
1816
+ attrs.push(`class="${xssFilter.attrValue(Array.from(vDom.classes).join(' '))}"`);
1817
+ }
1818
+ let attrStr = attrs.join(' ');
1819
+ attrStr = attrStr ? ' ' + attrStr : '';
1820
+ if (this.singleTagTest.test(vDom.tagName)) {
1821
+ return `<${vDom.tagName}${attrStr}>`;
1822
+ }
1823
+ const childHTML = vDom.children.map(child => {
1824
+ return this.vDomToHTMLString(child);
1825
+ }).join('');
1826
+ return [
1827
+ `<${vDom.tagName}${attrStr}>`,
1828
+ childHTML,
1829
+ `</${vDom.tagName}>`
1830
+ ].join('');
1831
+ }
1832
+ replaceEmpty(s, target) {
1833
+ return s.replace(/\s\s+/g, str => {
1834
+ return ' ' + Array.from({
1835
+ length: str.length - 1
1836
+ }).fill(target).join('');
1837
+ }).replace(/^\s|\s$/g, target);
1838
+ }
1839
+ };
1840
+ exports.OutputTranslator.singleTags = 'br,img,hr'.split(',');
1841
+ exports.OutputTranslator.simpleXSSFilter = {
1842
+ text(text) {
1843
+ return text.replace(/[><&]/g, str => {
1844
+ return {
1845
+ '<': '&lt;',
1846
+ '>': '&gt;',
1847
+ '&': '&amp;'
1848
+ }[str];
1849
+ });
1850
+ },
1851
+ attrName(text) {
1852
+ return text.replace(/[><"'&]/g, str => {
1853
+ return {
1854
+ '<': '&lt;',
1855
+ '>': '&gt;',
1856
+ '"': '&quot;',
1857
+ '\'': '&#x27;',
1858
+ '&': '&amp;'
1859
+ }[str];
1860
+ });
1861
+ },
1862
+ attrValue(text) {
1863
+ return text.replace(/["']/g, str => {
1864
+ return {
1865
+ '"': '&quot;',
1866
+ '\'': '&#x27;'
1867
+ }[str];
1868
+ });
1869
+ }
1870
+ };
1871
+ exports.OutputTranslator = OutputTranslator_1 = __decorate([
1872
+ di.Injectable()
1873
+ ], exports.OutputTranslator);
1874
+
1875
+ /**
1876
+ * Textbus PC 端默认按键绑定
1877
+ */
1878
+ class DefaultShortcut {
1879
+ setup(injector) {
1880
+ const selection = injector.get(core.Selection);
1881
+ const keyboard = injector.get(core.Keyboard);
1882
+ const history = injector.get(core.History);
1883
+ const commander = injector.get(core.Commander);
1884
+ keyboard.addShortcut({
1885
+ keymap: {
1886
+ key: 'Enter'
1887
+ },
1888
+ action: () => {
1889
+ commander.break();
1890
+ }
1891
+ });
1892
+ keyboard.addShortcut({
1893
+ keymap: {
1894
+ key: 'Enter',
1895
+ shiftKey: true
1896
+ },
1897
+ action: () => {
1898
+ const startOffset = selection.startOffset;
1899
+ const startSlot = selection.startSlot;
1900
+ const isToEnd = startOffset === startSlot.length || startSlot.isEmpty;
1901
+ const content = isToEnd ? '\n\n' : '\n';
1902
+ const isInserted = commander.insert(content);
1903
+ if (isInserted && isToEnd) {
1904
+ selection.setPosition(startSlot, startOffset + 1);
1905
+ }
1906
+ }
1907
+ });
1908
+ keyboard.addShortcut({
1909
+ keymap: {
1910
+ key: ['Delete', 'Backspace']
1911
+ },
1912
+ action: (key) => {
1913
+ commander.delete(key === 'Backspace');
1914
+ }
1915
+ });
1916
+ keyboard.addShortcut({
1917
+ keymap: {
1918
+ key: ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']
1919
+ },
1920
+ action: (key) => {
1921
+ switch (key) {
1922
+ case 'ArrowLeft':
1923
+ selection.toPrevious();
1924
+ break;
1925
+ case 'ArrowRight':
1926
+ selection.toNext();
1927
+ break;
1928
+ case 'ArrowUp':
1929
+ selection.toPreviousLine();
1930
+ break;
1931
+ case 'ArrowDown':
1932
+ selection.toNextLine();
1933
+ break;
1934
+ }
1935
+ }
1936
+ });
1937
+ keyboard.addShortcut({
1938
+ keymap: {
1939
+ key: ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'],
1940
+ shiftKey: true
1941
+ },
1942
+ action: (key) => {
1943
+ switch (key) {
1944
+ case 'ArrowLeft':
1945
+ selection.wrapToBefore();
1946
+ break;
1947
+ case 'ArrowRight':
1948
+ selection.wrapToAfter();
1949
+ break;
1950
+ case 'ArrowUp':
1951
+ selection.wrapToPreviousLine();
1952
+ break;
1953
+ case 'ArrowDown':
1954
+ selection.wrapToNextLine();
1955
+ break;
1956
+ }
1957
+ }
1958
+ });
1959
+ keyboard.addShortcut({
1960
+ keymap: {
1961
+ key: 'Tab'
1962
+ },
1963
+ action: () => {
1964
+ commander.insert(' ');
1965
+ }
1966
+ });
1967
+ keyboard.addShortcut({
1968
+ keymap: {
1969
+ key: 'a',
1970
+ ctrlKey: true
1971
+ },
1972
+ action: () => {
1973
+ selection.selectAll();
1974
+ }
1975
+ });
1976
+ keyboard.addShortcut({
1977
+ keymap: {
1978
+ key: 'c',
1979
+ ctrlKey: true
1980
+ },
1981
+ action: () => {
1982
+ commander.copy();
1983
+ }
1984
+ });
1985
+ keyboard.addShortcut({
1986
+ keymap: {
1987
+ key: 'x',
1988
+ ctrlKey: true
1989
+ },
1990
+ action: () => {
1991
+ commander.cut();
1992
+ }
1993
+ });
1994
+ keyboard.addShortcut({
1995
+ keymap: {
1996
+ key: 'z',
1997
+ ctrlKey: true
1998
+ },
1999
+ action: () => {
2000
+ history.back();
2001
+ }
2002
+ });
2003
+ keyboard.addShortcut({
2004
+ keymap: {
2005
+ key: 'z',
2006
+ ctrlKey: true,
2007
+ shiftKey: true
2008
+ },
2009
+ action: () => {
2010
+ history.forward();
2011
+ }
2012
+ });
2013
+ }
2014
+ }
2015
+
2016
+ const editorError = core.makeError('CoreEditor');
2017
+ /**
2018
+ * Textbus PC 端编辑器
2019
+ */
2020
+ class Viewer extends core.Starter {
2021
+ constructor(rootComponent, rootComponentLoader, options = {}) {
2022
+ const id = 'textbus-' + Number((Math.random() + '').substring(2)).toString(16);
2023
+ const { doc, mask, wrapper } = Viewer.createLayout(id, options.minHeight);
2024
+ const staticProviders = [{
2025
+ provide: EDITOR_OPTIONS,
2026
+ useValue: options
2027
+ }, {
2028
+ provide: VIEW_CONTAINER,
2029
+ useValue: wrapper
2030
+ }, {
2031
+ provide: VIEW_DOCUMENT,
2032
+ useValue: doc
2033
+ }, {
2034
+ provide: VIEW_MASK,
2035
+ useValue: mask
2036
+ }, {
2037
+ provide: core.NativeRenderer,
2038
+ useClass: exports.DomRenderer
2039
+ }, {
2040
+ provide: core.NativeSelectionBridge,
2041
+ useClass: exports.SelectionBridge
2042
+ }, {
2043
+ provide: Viewer,
2044
+ useFactory: () => this
2045
+ }];
2046
+ super(Object.assign(Object.assign({}, options), { plugins: [() => new DefaultShortcut(), ...(options.plugins || [])], providers: [
2047
+ ...(options.providers || []),
2048
+ ...staticProviders,
2049
+ exports.DomRenderer,
2050
+ exports.Parser,
2051
+ exports.Input,
2052
+ exports.Caret,
2053
+ exports.SelectionBridge,
2054
+ exports.OutputTranslator,
2055
+ exports.CollaborateCursor
2056
+ ], setup: options.setup }));
2057
+ this.rootComponent = rootComponent;
2058
+ this.rootComponentLoader = rootComponentLoader;
2059
+ this.options = options;
2060
+ /** 编辑器是否已销毁 */
2061
+ this.destroyed = false;
2062
+ /** 编辑器是否已准备好 */
2063
+ this.isReady = false;
2064
+ this.changeEvent = new stream.Subject();
2065
+ this.subs = [];
2066
+ this._isFocus = false;
2067
+ this.resourceNodes = [];
2068
+ this.focusEvent = new stream.Subject();
2069
+ this.blurEvent = new stream.Subject();
2070
+ this.saveEvent = new stream.Subject();
2071
+ this.styleSheet = '';
2072
+ this.scripts = [];
2073
+ this.links = [];
2074
+ this.id = id;
2075
+ this.workbench = wrapper;
2076
+ this.onChange = this.changeEvent.asObservable();
2077
+ this.onFocus = this.focusEvent.asObservable();
2078
+ this.onBlur = this.blurEvent.asObservable();
2079
+ this.onSave = this.saveEvent.asObservable();
2080
+ this.controller = this.get(core.Controller);
2081
+ }
2082
+ get readonly() {
2083
+ return this.controller.readonly;
2084
+ }
2085
+ set readonly(b) {
2086
+ this.controller.readonly = b;
2087
+ }
2088
+ isFocus() {
2089
+ return this._isFocus;
2090
+ }
2091
+ /**
2092
+ * 初始化编辑器
2093
+ * @param host 编辑器容器
2094
+ */
2095
+ mount(host) {
2096
+ const _super = Object.create(null, {
2097
+ mount: { get: () => super.mount }
2098
+ });
2099
+ return __awaiter(this, void 0, void 0, function* () {
2100
+ if (this.destroyed) {
2101
+ throw editorError('the editor instance is destroyed!');
2102
+ }
2103
+ if (this.destroyed) {
2104
+ return this;
2105
+ }
2106
+ const parser = this.get(exports.Parser);
2107
+ const factory = this.get(core.Factory);
2108
+ const doc = this.get(VIEW_DOCUMENT);
2109
+ const keyboard = this.get(core.Keyboard);
2110
+ keyboard.addShortcut({
2111
+ keymap: {
2112
+ key: 's',
2113
+ ctrlKey: true
2114
+ },
2115
+ action: () => {
2116
+ this.saveEvent.next();
2117
+ }
2118
+ });
2119
+ let component;
2120
+ const content = this.options.content;
2121
+ if (content) {
2122
+ if (typeof content === 'string') {
2123
+ component = parser.parseDoc(content, this.rootComponentLoader);
2124
+ }
2125
+ else {
2126
+ component = factory.createComponentByFactory(content, this.rootComponent);
2127
+ }
2128
+ }
2129
+ else {
2130
+ component = this.rootComponent.createInstance(this);
2131
+ }
2132
+ this.initDocStyleSheetsAndScripts(this.options);
2133
+ host.appendChild(this.workbench);
2134
+ yield _super.mount.call(this, doc, component);
2135
+ const renderer = this.get(core.Renderer);
2136
+ const caret = this.get(exports.Caret);
2137
+ this.subs.push(renderer.onViewUpdated.subscribe(() => {
2138
+ this.changeEvent.next();
2139
+ }), caret.onPositionChange.pipe(stream.map(p => !!p), stream.distinctUntilChanged()).subscribe(b => {
2140
+ if (b) {
2141
+ this._isFocus = true;
2142
+ this.focusEvent.next();
2143
+ }
2144
+ else {
2145
+ this._isFocus = false;
2146
+ this.blurEvent.next();
2147
+ }
2148
+ }));
2149
+ this.get(exports.Input);
2150
+ this.isReady = true;
2151
+ if (this.options.autoFocus) {
2152
+ this.get(exports.Input).onReady.then(() => {
2153
+ this.focus();
2154
+ });
2155
+ }
2156
+ return this;
2157
+ });
2158
+ }
2159
+ /**
2160
+ * 获取焦点
2161
+ */
2162
+ focus() {
2163
+ this.guardReady();
2164
+ const selection = this.get(core.Selection);
2165
+ const rootComponentRef = this.get(core.RootComponentRef);
2166
+ if (selection.commonAncestorSlot) {
2167
+ selection.restore();
2168
+ return;
2169
+ }
2170
+ const location = selection.findFirstPosition(rootComponentRef.component.slots.get(0));
2171
+ selection.setPosition(location.slot, location.offset);
2172
+ selection.restore();
2173
+ }
2174
+ /**
2175
+ * 取消编辑器焦点
2176
+ */
2177
+ blur() {
2178
+ if (this.isReady) {
2179
+ const selection = this.get(core.Selection);
2180
+ selection.unSelect();
2181
+ selection.restore();
2182
+ }
2183
+ }
2184
+ /**
2185
+ * 获取编辑器所有资源
2186
+ */
2187
+ getResources() {
2188
+ var _a;
2189
+ return {
2190
+ styleSheets: ((_a = this.options) === null || _a === void 0 ? void 0 : _a.styleSheets) || [],
2191
+ styleSheet: this.styleSheet,
2192
+ links: this.links,
2193
+ scripts: this.scripts
2194
+ };
2195
+ }
2196
+ /**
2197
+ * 获取 HTML 格式的内容
2198
+ */
2199
+ getContent() {
2200
+ this.guardReady();
2201
+ const outputRenderer = this.get(core.OutputRenderer);
2202
+ const outputTranslator = this.get(exports.OutputTranslator);
2203
+ const vDom = outputRenderer.render();
2204
+ return outputTranslator.transform(vDom);
2205
+ }
2206
+ /**
2207
+ * 获取 JSON 格式的内容
2208
+ */
2209
+ getJSON() {
2210
+ this.guardReady();
2211
+ const rootComponentRef = this.get(core.RootComponentRef);
2212
+ return rootComponentRef.component.toJSON();
2213
+ }
2214
+ /**
2215
+ * 销毁编辑器
2216
+ */
2217
+ destroy() {
2218
+ var _a;
2219
+ if (this.destroyed) {
2220
+ return;
2221
+ }
2222
+ this.destroyed = true;
2223
+ this.subs.forEach(i => i.unsubscribe());
2224
+ const types = [
2225
+ exports.Input,
2226
+ ];
2227
+ types.forEach(i => {
2228
+ this.get(i).destroy();
2229
+ });
2230
+ super.destroy();
2231
+ (_a = this.workbench.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.workbench);
2232
+ this.resourceNodes.forEach(node => {
2233
+ var _a;
2234
+ (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
2235
+ });
2236
+ }
2237
+ /**
2238
+ * 替换编辑的内容
2239
+ * @param content
2240
+ */
2241
+ replaceContent(content) {
2242
+ this.guardReady();
2243
+ const parser = this.get(exports.Parser);
2244
+ const factory = this.get(core.Factory);
2245
+ const rootComponentRef = this.get(core.RootComponentRef);
2246
+ const selection = this.get(core.Selection);
2247
+ const rootComponentLoader = this.rootComponentLoader;
2248
+ let component;
2249
+ if (typeof content === 'string') {
2250
+ component = parser.parseDoc(content, rootComponentLoader);
2251
+ }
2252
+ else {
2253
+ component = factory.createComponentByFactory(content, this.rootComponent);
2254
+ }
2255
+ selection.unSelect();
2256
+ rootComponentRef.component.slots.clean();
2257
+ rootComponentRef.component.slots.push(...component.slots.toArray());
2258
+ core.invokeListener(component, 'onDestroy');
2259
+ }
2260
+ guardReady() {
2261
+ if (this.destroyed) {
2262
+ throw editorError('the editor instance is destroyed!');
2263
+ }
2264
+ if (!this.isReady) {
2265
+ throw editorError('please wait for the editor to initialize before getting the content!');
2266
+ }
2267
+ }
2268
+ initDocStyleSheetsAndScripts(options) {
2269
+ var _a;
2270
+ const loaders = [];
2271
+ (_a = options.imports) === null || _a === void 0 ? void 0 : _a.forEach(module => {
2272
+ loaders.push(...(module.componentLoaders || []));
2273
+ });
2274
+ loaders.push(...(options.componentLoaders || []));
2275
+ const resources = loaders.filter(i => i.resources).map(i => i.resources);
2276
+ const docStyles = [];
2277
+ const editModeStyles = [];
2278
+ resources.forEach(metadata => {
2279
+ var _a, _b;
2280
+ if (Array.isArray(metadata.links)) {
2281
+ this.links.push(...metadata.links);
2282
+ }
2283
+ docStyles.push(((_a = metadata.styles) === null || _a === void 0 ? void 0 : _a.join('')) || '');
2284
+ editModeStyles.push(((_b = metadata.editModeStyles) === null || _b === void 0 ? void 0 : _b.join('')) || '');
2285
+ });
2286
+ this.links.forEach(link => {
2287
+ const linkEle = document.createElement('link');
2288
+ Object.assign(linkEle, link);
2289
+ this.resourceNodes.push(linkEle);
2290
+ document.head.appendChild(linkEle);
2291
+ });
2292
+ const styleEl = document.createElement('style');
2293
+ docStyles.push(...(options.styleSheets || []));
2294
+ editModeStyles.push(`#${this.id} *::selection{background-color: rgba(18, 150, 219, .2); color:inherit}`, ...(options.editingStyleSheets || []));
2295
+ this.styleSheet = Viewer.cssMin(docStyles.join(''));
2296
+ styleEl.innerHTML = this.styleSheet + Viewer.cssMin(editModeStyles.join(''));
2297
+ this.resourceNodes.push(styleEl);
2298
+ document.head.append(styleEl);
2299
+ resources.filter(i => { var _a; return (_a = i.scripts) === null || _a === void 0 ? void 0 : _a.length; }).map(i => i.scripts).flat().forEach(src => {
2300
+ if (src) {
2301
+ const script = document.createElement('script');
2302
+ script.src = src;
2303
+ this.scripts.push(src);
2304
+ document.head.appendChild(script);
2305
+ this.resourceNodes.push(script);
2306
+ }
2307
+ });
2308
+ }
2309
+ static createLayout(id, minHeight = '100%') {
2310
+ const doc = createElement('div', {
2311
+ styles: {
2312
+ cursor: 'text',
2313
+ wordBreak: 'break-all',
2314
+ boxSizing: 'border-box',
2315
+ minHeight,
2316
+ flex: 1
2317
+ },
2318
+ attrs: {
2319
+ 'data-textbus-view': VIEW_DOCUMENT,
2320
+ },
2321
+ props: {
2322
+ id
2323
+ }
2324
+ });
2325
+ const mask = createElement('div', {
2326
+ attrs: {
2327
+ 'data-textbus-view': VIEW_MASK,
2328
+ },
2329
+ styles: {
2330
+ position: 'absolute',
2331
+ left: 0,
2332
+ right: 0,
2333
+ top: 0,
2334
+ bottom: 0,
2335
+ zIndex: 1,
2336
+ pointerEvents: 'none',
2337
+ overflow: 'hidden'
2338
+ }
2339
+ });
2340
+ const wrapper = createElement('div', {
2341
+ attrs: {
2342
+ 'data-textbus-view': VIEW_CONTAINER,
2343
+ },
2344
+ styles: {
2345
+ display: 'flex',
2346
+ minHeight: '100%',
2347
+ position: 'relative',
2348
+ flexDirection: 'column'
2349
+ },
2350
+ children: [doc, mask]
2351
+ });
2352
+ return {
2353
+ wrapper,
2354
+ doc,
2355
+ mask
2356
+ };
2357
+ }
2358
+ static cssMin(str) {
2359
+ return str
2360
+ .replace(/\s*(?=[>{}:;,[])/g, '')
2361
+ .replace(/([>{}:;,])\s*/g, '$1')
2362
+ .replace(/;}/g, '}').replace(/\s+/, ' ').trim();
2363
+ }
2364
+ }
2365
+
2366
+ exports.CollaborateSelectionAwarenessDelegate = CollaborateSelectionAwarenessDelegate;
2367
+ exports.DefaultShortcut = DefaultShortcut;
2368
+ exports.EDITOR_OPTIONS = EDITOR_OPTIONS;
2369
+ exports.VIEW_CONTAINER = VIEW_CONTAINER;
2370
+ exports.VIEW_DOCUMENT = VIEW_DOCUMENT;
2371
+ exports.VIEW_MASK = VIEW_MASK;
2372
+ exports.Viewer = Viewer;
2373
+ exports.createElement = createElement;
2374
+ exports.createTextNode = createTextNode;
2375
+ exports.getLayoutRectByRange = getLayoutRectByRange;
2376
+ exports.isMac = isMac;
2377
+ exports.isSafari = isSafari;
2378
+ exports.isWindows = isWindows;