@textbus/platform-browser 3.5.0 → 4.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import 'reflect-metadata';
2
- import { InjectionToken, Injectable, Inject, Injector, Optional } from '@tanbo/di';
3
- import { VTextNode, VElement, Controller, Selection, RootComponentRef, Renderer, Scheduler, Slot, ContentType, Event, invokeListener, Keyboard, Commander, makeError, Starter, NativeRenderer, NativeSelectionBridge, OutputRenderer, History, Registry } from '@textbus/core';
2
+ import { Slot, ViewAdapter, createBidirectionalMapping, VElement, VTextNode, ComponentInstance, Controller, Selection, RootComponentRef, ContentType, Event, invokeListener, Keyboard, Commander, Scheduler, NativeSelectionBridge } from '@textbus/core';
3
+ import { InjectionToken, Injectable, Inject, Injector, Optional } from '@viewfly/core';
4
4
  import { Subject, filter, fromEvent, Subscription, distinctUntilChanged, merge, map, Observable } from '@tanbo/stream';
5
5
 
6
6
  function createElement(tagName, options = {}) {
@@ -118,6 +118,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
118
118
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
119
119
  PERFORMANCE OF THIS SOFTWARE.
120
120
  ***************************************************************************** */
121
+ /* global Reflect, Promise, SuppressedError, Symbol */
122
+
121
123
 
122
124
  function __decorate(decorators, target, key, desc) {
123
125
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -134,15 +136,10 @@ function __metadata(metadataKey, metadataValue) {
134
136
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
135
137
  }
136
138
 
137
- function __awaiter(thisArg, _arguments, P, generator) {
138
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
139
- return new (P || (P = Promise))(function (resolve, reject) {
140
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
141
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
142
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
143
- step((generator = generator.apply(thisArg, _arguments || [])).next());
144
- });
145
- }
139
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
140
+ var e = new Error(message);
141
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
142
+ };
146
143
 
147
144
  /**
148
145
  * 编辑器可选项依赖注入 token
@@ -161,34 +158,289 @@ const VIEW_DOCUMENT = new InjectionToken('VIEW_DOCUMENT');
161
158
  */
162
159
  const VIEW_MASK = new InjectionToken('VIEW_MASK');
163
160
 
161
+ var Parser_1;
162
+ /**
163
+ * 用于解析 HTML,并把 HTML 内容转换为 Textbus 可以支持的组件或插槽数据
164
+ */
165
+ let Parser = Parser_1 = class Parser {
166
+ static parseHTML(html) {
167
+ return new DOMParser().parseFromString(html, 'text/html').body;
168
+ }
169
+ constructor(options, injector) {
170
+ this.options = options;
171
+ this.injector = injector;
172
+ const componentLoaders = [
173
+ ...(options.componentLoaders || [])
174
+ ];
175
+ const formatLoaders = [
176
+ ...(options.formatLoaders || [])
177
+ ];
178
+ const attributeLoaders = [
179
+ ...(options.attributeLoaders || [])
180
+ ];
181
+ // options.imports?.forEach(i => {
182
+ // componentLoaders.push(...(i.componentLoaders || []))
183
+ // formatLoaders.push(...(i.formatLoaders || []))
184
+ // })
185
+ this.componentLoaders = componentLoaders;
186
+ this.formatLoaders = formatLoaders;
187
+ this.attributeLoaders = attributeLoaders;
188
+ }
189
+ /**
190
+ * 使用指定的组件加载器解析一段 HTML 字符串
191
+ * @param html
192
+ * @param rootComponentLoader
193
+ */
194
+ parseDoc(html, rootComponentLoader) {
195
+ const element = Parser_1.parseHTML(html);
196
+ return rootComponentLoader.read(element, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
197
+ return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
198
+ });
199
+ }
200
+ /**
201
+ * 将一段 HTML 解析到指定插槽
202
+ * @param html
203
+ * @param rootSlot
204
+ */
205
+ parse(html, rootSlot) {
206
+ const element = Parser_1.parseHTML(html);
207
+ return this.readFormats(element, rootSlot);
208
+ }
209
+ readComponent(el, slot) {
210
+ if (el.nodeType === Node.ELEMENT_NODE) {
211
+ if (el.tagName === 'BR') {
212
+ slot.insert('\n');
213
+ return;
214
+ }
215
+ for (const t of this.componentLoaders) {
216
+ if (t.match(el)) {
217
+ const result = t.read(el, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
218
+ return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
219
+ });
220
+ if (!result) {
221
+ return;
222
+ }
223
+ if (result instanceof Slot) {
224
+ result.toDelta().forEach(i => slot.insert(i.insert, i.formats));
225
+ return;
226
+ }
227
+ slot.insert(result);
228
+ return;
229
+ }
230
+ }
231
+ this.readFormats(el, slot);
232
+ }
233
+ else if (el.nodeType === Node.TEXT_NODE) {
234
+ this.readText(slot, el);
235
+ }
236
+ }
237
+ readText(slot, el) {
238
+ const textContent = el.textContent;
239
+ if (/^\s*[\r\n\u200b]+\s*$/.test(textContent)) {
240
+ return;
241
+ }
242
+ slot.insert(textContent);
243
+ }
244
+ readFormats(el, slot) {
245
+ const formats = this.formatLoaders.filter(f => {
246
+ return f.match(el);
247
+ }).map(f => {
248
+ return f.read(el);
249
+ });
250
+ const startIndex = slot.index;
251
+ let startNode = el.firstChild;
252
+ while (startNode) {
253
+ this.readComponent(startNode, slot);
254
+ startNode = startNode.nextSibling;
255
+ }
256
+ const endIndex = slot.index;
257
+ this.applyFormats(slot, formats.map(i => {
258
+ return {
259
+ formatter: i.formatter,
260
+ value: i.value,
261
+ startIndex,
262
+ endIndex
263
+ };
264
+ }));
265
+ slot.retain(endIndex);
266
+ return slot;
267
+ }
268
+ readSlot(childSlot, slotRootElement, slotContentElement) {
269
+ if (slotRootElement.nodeType === Node.ELEMENT_NODE) {
270
+ this.attributeLoaders.filter(a => {
271
+ return a.match(slotRootElement);
272
+ }).forEach(a => {
273
+ const r = a.read(slotRootElement);
274
+ childSlot.setAttribute(r.attribute, r.value);
275
+ });
276
+ }
277
+ if (slotContentElement.nodeType === Node.ELEMENT_NODE) {
278
+ this.readFormats(slotContentElement, childSlot);
279
+ }
280
+ else {
281
+ this.readText(childSlot, slotContentElement);
282
+ }
283
+ return childSlot;
284
+ }
285
+ applyFormats(slot, formatItems) {
286
+ slot.background(() => {
287
+ formatItems.forEach(i => {
288
+ slot.retain(i.startIndex);
289
+ slot.retain(i.endIndex - i.startIndex, i.formatter, i.value);
290
+ });
291
+ });
292
+ }
293
+ };
294
+ Parser = Parser_1 = __decorate([
295
+ Injectable(),
296
+ __param(0, Inject(EDITOR_OPTIONS)),
297
+ __metadata("design:paramtypes", [Object, Injector])
298
+ ], Parser);
299
+
164
300
  class Input {
165
301
  }
166
302
 
303
+ /**
304
+ * Textbus PC 端浏览器渲染能力实现
305
+ */
306
+ class DomAdapter extends ViewAdapter {
307
+ constructor(mount) {
308
+ super();
309
+ this.mount = mount;
310
+ this.host = createElement('div', {
311
+ styles: {
312
+ cursor: 'text',
313
+ wordBreak: 'break-all',
314
+ boxSizing: 'border-box',
315
+ flex: 1,
316
+ outline: 'none'
317
+ },
318
+ attrs: {
319
+ 'data-textbus-view': VIEW_DOCUMENT,
320
+ },
321
+ props: {
322
+ id: 'textbus-' + Number((Math.random() + '').substring(2)).toString(16)
323
+ }
324
+ });
325
+ this.componentRootElementCaches = new WeakMap();
326
+ this.slotRootNativeElementCaches = createBidirectionalMapping(a => {
327
+ return a instanceof Slot;
328
+ });
329
+ this.slotRootVElementCaches = new WeakMap();
330
+ }
331
+ render(rootComponent) {
332
+ const view = this.componentRender(rootComponent);
333
+ return this.mount(this.host, view);
334
+ }
335
+ copy() {
336
+ document.execCommand('copy');
337
+ }
338
+ getNativeNodeByComponent(component) {
339
+ return this.componentRootElementCaches.get(component) || null;
340
+ }
341
+ getNativeNodeBySlot(slot) {
342
+ return this.slotRootNativeElementCaches.get(slot) || null;
343
+ }
344
+ /**
345
+ * 获取插槽内容节点集合
346
+ * @param slot
347
+ */
348
+ getNodesBySlot(slot) {
349
+ const rootNativeNode = this.getNativeNodeBySlot(slot);
350
+ if (!rootNativeNode) {
351
+ return [];
352
+ }
353
+ const rootVNode = this.slotRootVElementCaches.get(slot);
354
+ const getNodes = (vElement, nativeNode, result) => {
355
+ if (vElement.location) {
356
+ result.push(nativeNode);
357
+ }
358
+ for (let i = 0; i < vElement.children.length; i++) {
359
+ const vChild = vElement.children[i];
360
+ const nativeChild = nativeNode.childNodes[i];
361
+ if (vChild instanceof VElement) {
362
+ getNodes(vChild, nativeChild, result);
363
+ }
364
+ else if (vChild instanceof VTextNode) {
365
+ result.push(nativeChild);
366
+ }
367
+ else {
368
+ result.push(this.getNativeNodeByComponent(vChild));
369
+ }
370
+ }
371
+ return result;
372
+ };
373
+ return getNodes(rootVNode, rootNativeNode, []);
374
+ }
375
+ /**
376
+ * 获取原生节点的原始数据在文档中的位置
377
+ * @param node
378
+ */
379
+ getLocationByNativeNode(node) {
380
+ let slotRootNode = node;
381
+ while (!this.slotRootNativeElementCaches.get(slotRootNode)) {
382
+ slotRootNode = slotRootNode.parentNode;
383
+ if (!slotRootNode) {
384
+ return null;
385
+ }
386
+ }
387
+ const slot = this.slotRootNativeElementCaches.get(slotRootNode);
388
+ const rootVNode = this.slotRootVElementCaches.get(slot);
389
+ const getLocation = (target, tree, vNodeTree) => {
390
+ if (target === tree) {
391
+ return Object.assign({}, vNodeTree.location);
392
+ }
393
+ const childNodes = tree.childNodes;
394
+ for (let i = 0; i < childNodes.length; i++) {
395
+ const child = vNodeTree.children[i];
396
+ const nativeChild = tree.childNodes[i];
397
+ if (nativeChild === target) {
398
+ if (child instanceof ComponentInstance) {
399
+ const index = child.parent.indexOf(child);
400
+ return {
401
+ slot: child.parent,
402
+ startIndex: index,
403
+ endIndex: index + 1
404
+ };
405
+ }
406
+ return child.location;
407
+ }
408
+ else if (child instanceof VElement) {
409
+ let r = null;
410
+ if (nativeChild.nodeType === Node.ELEMENT_NODE) {
411
+ r = getLocation(target, nativeChild, child);
412
+ }
413
+ if (r) {
414
+ return r;
415
+ }
416
+ }
417
+ }
418
+ return null;
419
+ };
420
+ return getLocation(node, slotRootNode, rootVNode);
421
+ }
422
+ }
423
+
167
424
  /**
168
425
  * Textbus PC 端选区桥接实现
169
426
  */
170
427
  let SelectionBridge = class SelectionBridge {
171
- constructor(config, injector, controller, selection, rootComponentRef, input, renderer) {
428
+ constructor(config, injector, controller, selection, rootComponentRef, input, domAdapter) {
172
429
  this.config = config;
173
- this.injector = injector;
174
- this.controller = controller;
175
430
  this.selection = selection;
176
431
  this.rootComponentRef = rootComponentRef;
177
432
  this.input = input;
178
- this.renderer = renderer;
433
+ this.domAdapter = domAdapter;
179
434
  this.nativeSelection = document.getSelection();
180
- this.selectionMaskElement = createElement('style');
181
435
  this.selectionChangeEvent = new Subject();
182
436
  this.subs = [];
183
437
  this.connector = null;
184
438
  this.ignoreSelectionChange = false;
185
439
  this.changeFromUser = false;
186
440
  this.docContainer = injector.get(VIEW_DOCUMENT);
187
- this.maskContainer = injector.get(VIEW_MASK);
188
441
  this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(filter(() => {
189
442
  return !controller.readonly;
190
443
  }));
191
- document.head.appendChild(this.selectionMaskElement);
192
444
  this.sub = this.onSelectionChange.subscribe((r) => {
193
445
  if (r) {
194
446
  input.focus(r, this.changeFromUser);
@@ -484,22 +736,14 @@ let SelectionBridge = class SelectionBridge {
484
736
  const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
485
737
  if (!this.docContainer.contains(selection.focusNode)) {
486
738
  if (isFocusEnd) {
487
- const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.first);
488
- if (!vEle) {
489
- return;
490
- }
491
- const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
739
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.first);
492
740
  if (!nativeNode) {
493
741
  return;
494
742
  }
495
743
  nativeRange.setEndAfter(nativeNode.lastChild);
496
744
  }
497
745
  else {
498
- const vEle = this.renderer.getVNodeBySlot(this.rootComponentRef.component.slots.last);
499
- if (!vEle) {
500
- return;
501
- }
502
- const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
746
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.last);
503
747
  if (!nativeNode) {
504
748
  return;
505
749
  }
@@ -556,30 +800,28 @@ let SelectionBridge = class SelectionBridge {
556
800
  }
557
801
  findSelectedNodeAndOffset(slot, offset) {
558
802
  const prev = slot.getContentAtIndex(offset - 1);
559
- const vNodes = this.renderer.getVNodesBySlot(slot);
803
+ const nodes = this.domAdapter.getNodesBySlot(slot);
560
804
  if (prev) {
561
805
  if (typeof prev !== 'string') {
562
- const vNode = this.renderer.getVNodeByComponent(prev);
563
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
806
+ const nativeNode = this.domAdapter.getNativeNodeByComponent(prev);
564
807
  return {
565
808
  node: nativeNode.parentNode,
566
809
  offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode) + 1
567
810
  };
568
811
  }
569
812
  else if (prev === '\n') {
570
- for (const vNode of vNodes) {
571
- if (vNode instanceof VTextNode) {
813
+ for (const node of nodes) {
814
+ if (node instanceof Text) {
572
815
  continue;
573
816
  }
574
- if (vNode.tagName === 'br') {
575
- const position = this.renderer.getLocationByVNode(vNode);
817
+ if (node.nodeName === 'BR') {
818
+ const position = this.domAdapter.getLocationByNativeNode(node);
576
819
  if (position) {
577
820
  if (position.endIndex === offset) {
578
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
579
- const parentNode = nativeNode.parentNode;
821
+ const parentNode = node.parentNode;
580
822
  return {
581
823
  node: parentNode,
582
- offset: Array.from(parentNode.childNodes).indexOf(nativeNode) + 1
824
+ offset: Array.from(parentNode.childNodes).indexOf(node) + 1
583
825
  };
584
826
  }
585
827
  }
@@ -589,36 +831,33 @@ let SelectionBridge = class SelectionBridge {
589
831
  }
590
832
  const current = slot.getContentAtIndex(offset);
591
833
  if (current && typeof current !== 'string') {
592
- const vNode = this.renderer.getVNodeByComponent(current);
593
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
834
+ const nativeNode = this.domAdapter.getNativeNodeByComponent(current);
594
835
  return {
595
836
  node: nativeNode.parentNode,
596
837
  offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
597
838
  };
598
839
  }
599
- for (const vNode of vNodes) {
600
- if (vNode instanceof VElement) {
601
- if (vNode.tagName === 'br') {
602
- const position = this.renderer.getLocationByVNode(vNode);
840
+ for (const node of nodes) {
841
+ if (node instanceof Element) {
842
+ if (node.tagName === 'BR') {
843
+ const position = this.domAdapter.getLocationByNativeNode(node);
603
844
  if (position) {
604
845
  if (position.startIndex === offset) {
605
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
606
- const parentNode = nativeNode.parentNode;
846
+ const parentNode = node.parentNode;
607
847
  return {
608
848
  node: parentNode,
609
- offset: Array.from(parentNode.childNodes).indexOf(nativeNode)
849
+ offset: Array.from(parentNode.childNodes).indexOf(node)
610
850
  };
611
851
  }
612
852
  }
613
853
  }
614
854
  continue;
615
855
  }
616
- const position = this.renderer.getLocationByVNode(vNode);
856
+ const position = this.domAdapter.getLocationByNativeNode(node);
617
857
  if (position) {
618
858
  if (offset >= position.startIndex && offset <= position.endIndex) {
619
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
620
859
  return {
621
- node: nativeNode,
860
+ node: node,
622
861
  offset: offset - position.startIndex
623
862
  };
624
863
  }
@@ -629,10 +868,10 @@ let SelectionBridge = class SelectionBridge {
629
868
  getCorrectedPosition(node, offset, toAfter, excludeNodes = []) {
630
869
  excludeNodes.push(node);
631
870
  if (node.nodeType === Node.ELEMENT_NODE) {
632
- const containerPosition = this.renderer.getLocationByNativeNode(node);
871
+ const containerPosition = this.domAdapter.getLocationByNativeNode(node);
633
872
  const childNode = node.childNodes[offset];
634
873
  if (childNode) {
635
- const childPosition = this.renderer.getLocationByNativeNode(childNode);
874
+ const childPosition = this.domAdapter.getLocationByNativeNode(childNode);
636
875
  if (childPosition) {
637
876
  if (containerPosition) {
638
877
  return {
@@ -646,7 +885,7 @@ let SelectionBridge = class SelectionBridge {
646
885
  }
647
886
  const prevNode = node.childNodes[offset - 1];
648
887
  if (prevNode) {
649
- const prevPosition = this.renderer.getLocationByNativeNode(prevNode);
888
+ const prevPosition = this.domAdapter.getLocationByNativeNode(prevNode);
650
889
  if (prevPosition && containerPosition) {
651
890
  return {
652
891
  slot: prevPosition.slot,
@@ -667,7 +906,7 @@ let SelectionBridge = class SelectionBridge {
667
906
  return this.findFocusNodeByParent(node, toAfter, excludeNodes);
668
907
  }
669
908
  else if (node.nodeType === Node.TEXT_NODE) {
670
- const containerPosition = this.renderer.getLocationByNativeNode(node);
909
+ const containerPosition = this.domAdapter.getLocationByNativeNode(node);
671
910
  if (containerPosition) {
672
911
  return {
673
912
  slot: containerPosition.slot,
@@ -691,7 +930,7 @@ let SelectionBridge = class SelectionBridge {
691
930
  return this.findFocusNodeByParent(node, toAfter, excludeNodes);
692
931
  }
693
932
  excludeNodes.push(node);
694
- const position = this.renderer.getLocationByNativeNode(node);
933
+ const position = this.domAdapter.getLocationByNativeNode(node);
695
934
  if (position) {
696
935
  return {
697
936
  slot: position.slot,
@@ -711,7 +950,7 @@ let SelectionBridge = class SelectionBridge {
711
950
  findFocusNodeByParent(node, toAfter, excludeNodes) {
712
951
  const parentNode = node.parentNode;
713
952
  if (parentNode) {
714
- const parentPosition = this.renderer.getLocationByNativeNode(parentNode);
953
+ const parentPosition = this.domAdapter.getLocationByNativeNode(parentNode);
715
954
  if (parentPosition) {
716
955
  return {
717
956
  slot: parentPosition.slot,
@@ -732,660 +971,46 @@ SelectionBridge = __decorate([
732
971
  Selection,
733
972
  RootComponentRef,
734
973
  Input,
735
- Renderer])
974
+ DomAdapter])
736
975
  ], SelectionBridge);
737
976
 
738
- /**
739
- * 远程光标绘制范围计算代理类,可用于定制特定场景下的远程选区绘制,如表格有选区,不会遵守常见的文档流形式
740
- */
741
- class CollaborateSelectionAwarenessDelegate {
742
- }
743
- /**
744
- * 协作光标绘制类
745
- */
746
- let CollaborateCursor = class CollaborateCursor {
747
- constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
748
- this.injector = injector;
749
- this.nativeSelection = nativeSelection;
977
+ const iframeHTML = `
978
+ <!DOCTYPE html>
979
+ <html>
980
+ <head>
981
+ <meta charset="UTF-8">
982
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
983
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
984
+ <title>Textbus</title>
985
+ <style>
986
+ html {position: fixed; left:0; overflow: hidden}
987
+ html, body{height: 100%;width:100%}
988
+ body{margin:0; overflow: hidden}
989
+ textarea{width: 2000px;height: 100%;opacity: 0; padding: 0; outline: none; border: none; position: absolute; left:0; top:0;}
990
+ </style>
991
+ </head>
992
+ <body>
993
+ </body>
994
+ </html>
995
+ `;
996
+ class ExperimentalCaret {
997
+ get rect() {
998
+ return this.caret.getBoundingClientRect();
999
+ }
1000
+ set display(v) {
1001
+ this._display = v;
1002
+ this.caret.style.visibility = v ? 'visible' : 'hidden';
1003
+ }
1004
+ get display() {
1005
+ return this._display;
1006
+ }
1007
+ constructor(scheduler, editorMask) {
750
1008
  this.scheduler = scheduler;
751
- this.selection = selection;
752
- this.awarenessDelegate = awarenessDelegate;
753
- this.host = createElement('div', {
1009
+ this.editorMask = editorMask;
1010
+ this.compositionState = null;
1011
+ this.compositionElement = createElement('span', {
754
1012
  styles: {
755
- position: 'absolute',
756
- left: 0,
757
- top: 0,
758
- width: '100%',
759
- height: '100%',
760
- pointerEvents: 'none',
761
- zIndex: 1
762
- }
763
- });
764
- this.canvasContainer = createElement('div', {
765
- styles: {
766
- position: 'absolute',
767
- left: 0,
768
- top: 0,
769
- width: '100%',
770
- height: '100%',
771
- overflow: 'hidden'
772
- }
773
- });
774
- this.canvas = createElement('canvas', {
775
- styles: {
776
- position: 'absolute',
777
- opacity: 0.5,
778
- left: 0,
779
- top: 0,
780
- width: '100%',
781
- height: document.documentElement.clientHeight + 'px',
782
- pointerEvents: 'none',
783
- }
784
- });
785
- this.context = this.canvas.getContext('2d');
786
- this.tooltips = createElement('div', {
787
- styles: {
788
- position: 'absolute',
789
- left: 0,
790
- top: 0,
791
- width: '100%',
792
- height: '100%',
793
- pointerEvents: 'none',
794
- fontSize: '12px',
795
- zIndex: 10
796
- }
797
- });
798
- this.onRectsChange = new Subject();
799
- this.subscription = new Subscription();
800
- this.currentSelection = [];
801
- this.container = injector.get(VIEW_CONTAINER);
802
- this.canvasContainer.append(this.canvas);
803
- this.host.append(this.canvasContainer, this.tooltips);
804
- this.container.prepend(this.host);
805
- this.subscription.add(this.onRectsChange.subscribe(rects => {
806
- for (const rect of rects) {
807
- this.context.fillStyle = rect.color;
808
- this.context.beginPath();
809
- this.context.rect(rect.left, rect.top, rect.width, rect.height);
810
- this.context.fill();
811
- this.context.closePath();
812
- }
813
- }), fromEvent(window, 'resize').subscribe(() => {
814
- this.canvas.style.height = document.documentElement.clientHeight + 'px';
815
- this.refresh();
816
- }), this.scheduler.onDocChanged.subscribe(() => {
817
- this.refresh();
818
- }));
819
- }
820
- /**
821
- * 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
822
- */
823
- refresh() {
824
- this.draw(this.currentSelection);
825
- }
826
- destroy() {
827
- this.subscription.unsubscribe();
828
- }
829
- /**
830
- * 根据远程用户光标位置,绘制协作光标
831
- * @param paths
832
- */
833
- draw(paths) {
834
- this.currentSelection = paths;
835
- const containerRect = this.container.getBoundingClientRect();
836
- this.canvas.style.top = containerRect.top * -1 + 'px';
837
- this.canvas.width = this.canvas.offsetWidth;
838
- this.canvas.height = this.canvas.offsetHeight;
839
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
840
- const users = [];
841
- paths.filter(i => {
842
- return i.paths.anchor.length && i.paths.focus.length;
843
- }).forEach(item => {
844
- const anchorPaths = [...item.paths.anchor];
845
- const focusPaths = [...item.paths.focus];
846
- const anchorOffset = anchorPaths.pop();
847
- const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
848
- const focusOffset = focusPaths.pop();
849
- const focusSlot = this.selection.findSlotByPaths(focusPaths);
850
- if (!anchorSlot || !focusSlot) {
851
- return;
852
- }
853
- const { focus, anchor } = this.nativeSelection.getPositionByRange({
854
- focusOffset,
855
- anchorOffset,
856
- focusSlot,
857
- anchorSlot
858
- });
859
- if (!focus || !anchor) {
860
- return;
861
- }
862
- const nativeRange = document.createRange();
863
- nativeRange.setStart(anchor.node, anchor.offset);
864
- nativeRange.setEnd(focus.node, focus.offset);
865
- if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
866
- nativeRange.setStart(focus.node, focus.offset);
867
- nativeRange.setEnd(anchor.node, anchor.offset);
868
- }
869
- let rects = false;
870
- if (this.awarenessDelegate) {
871
- rects = this.awarenessDelegate.getRects({
872
- focusOffset,
873
- anchorOffset,
874
- focusSlot,
875
- anchorSlot
876
- }, nativeRange);
877
- }
878
- if (!rects) {
879
- rects = nativeRange.getClientRects();
880
- }
881
- const selectionRects = [];
882
- for (let i = rects.length - 1; i >= 0; i--) {
883
- const rect = rects[i];
884
- selectionRects.push({
885
- id: item.id,
886
- color: item.color,
887
- username: item.username,
888
- left: rect.left - containerRect.left,
889
- top: rect.top,
890
- width: rect.width,
891
- height: rect.height,
892
- });
893
- }
894
- this.onRectsChange.next(selectionRects);
895
- const cursorRange = nativeRange.cloneRange();
896
- cursorRange.setStart(focus.node, focus.offset);
897
- cursorRange.collapse(true);
898
- const cursorRect = getLayoutRectByRange(cursorRange);
899
- const rect = {
900
- id: item.id,
901
- username: item.username,
902
- color: item.color,
903
- left: cursorRect.left - containerRect.left,
904
- top: cursorRect.top - containerRect.top,
905
- width: 1,
906
- height: cursorRect.height
907
- };
908
- if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
909
- return;
910
- }
911
- users.push(rect);
912
- });
913
- this.drawUserCursor(users);
914
- }
915
- drawUserCursor(rects) {
916
- for (let i = 0; i < rects.length; i++) {
917
- const rect = rects[i];
918
- const { cursor, userTip, anchor } = this.getUserCursor(i);
919
- Object.assign(cursor.style, {
920
- left: rect.left + 'px',
921
- top: rect.top + 'px',
922
- width: rect.width + 'px',
923
- height: rect.height + 'px',
924
- background: rect.color,
925
- display: 'block'
926
- });
927
- anchor.style.background = rect.color;
928
- userTip.innerText = rect.username;
929
- userTip.style.background = rect.color;
930
- }
931
- for (let i = rects.length; i < this.tooltips.children.length; i++) {
932
- this.tooltips.removeChild(this.tooltips.children[i]);
933
- }
934
- }
935
- getUserCursor(index) {
936
- let child = this.tooltips.children[index];
937
- if (child) {
938
- const anchor = child.children[0];
939
- return {
940
- cursor: child,
941
- anchor,
942
- userTip: anchor.children[0]
943
- };
944
- }
945
- const userTip = createElement('span', {
946
- styles: {
947
- position: 'absolute',
948
- left: '50%',
949
- transform: 'translateX(-50%)',
950
- marginBottom: '2px',
951
- bottom: '100%',
952
- whiteSpace: 'nowrap',
953
- color: '#fff',
954
- boxShadow: '0 1px 2px rgba(0,0,0,.1)',
955
- opacity: 0.8,
956
- borderRadius: '3px',
957
- padding: '3px 5px',
958
- pointerEvents: 'none',
959
- }
960
- });
961
- const anchor = createElement('span', {
962
- styles: {
963
- position: 'absolute',
964
- top: '-2px',
965
- left: '-2px',
966
- width: '5px',
967
- height: '5px',
968
- borderRadius: '50%',
969
- pointerEvents: 'auto',
970
- pointer: 'cursor',
971
- },
972
- children: [userTip]
973
- });
974
- child = createElement('span', {
975
- styles: {
976
- position: 'absolute',
977
- },
978
- children: [
979
- anchor
980
- ]
981
- });
982
- this.tooltips.append(child);
983
- return {
984
- cursor: child,
985
- anchor,
986
- userTip
987
- };
988
- }
989
- };
990
- CollaborateCursor = __decorate([
991
- Injectable(),
992
- __param(4, Optional()),
993
- __metadata("design:paramtypes", [Injector,
994
- SelectionBridge,
995
- Scheduler,
996
- Selection,
997
- CollaborateSelectionAwarenessDelegate])
998
- ], CollaborateCursor);
999
-
1000
- var DomRenderer_1;
1001
- /**
1002
- * Textbus PC 端浏览器渲染能力实现
1003
- */
1004
- let DomRenderer = DomRenderer_1 = class DomRenderer {
1005
- constructor() {
1006
- this.isSVG = new RegExp(`^(${[
1007
- // 'a',
1008
- 'animate',
1009
- 'animateMotion',
1010
- 'animateTransform',
1011
- 'circle',
1012
- 'clipPath',
1013
- 'defs',
1014
- 'desc',
1015
- 'ellipse',
1016
- 'feBlend',
1017
- 'feColorMatrix',
1018
- 'feComponentTransfer',
1019
- 'feComposite',
1020
- 'feConvolveMatrix',
1021
- 'feDiffuseLighting',
1022
- 'feDisplacementMap',
1023
- 'feDistantLight',
1024
- 'feDropShadow',
1025
- 'feFlood',
1026
- 'feFuncA',
1027
- 'feFuncB',
1028
- 'feFuncG',
1029
- 'feFuncR',
1030
- 'feGaussianBlur',
1031
- 'feImage',
1032
- 'feMerge',
1033
- 'feMergeNode',
1034
- 'feMorphology',
1035
- 'feOffset',
1036
- 'fePointLight',
1037
- 'feSpecularLighting',
1038
- 'feSpotLight',
1039
- 'feTile',
1040
- 'feTurbulence',
1041
- 'filter',
1042
- 'foreignObject',
1043
- 'g',
1044
- 'image',
1045
- 'line',
1046
- 'linearGradient',
1047
- 'marker',
1048
- 'mask',
1049
- 'metadata',
1050
- 'mpath',
1051
- 'path',
1052
- 'pattern',
1053
- 'polygon',
1054
- 'polyline',
1055
- 'radialGradient',
1056
- 'rect',
1057
- // 'script',
1058
- 'set',
1059
- 'stop',
1060
- // 'style',
1061
- 'svg',
1062
- 'switch',
1063
- 'symbol',
1064
- 'text',
1065
- 'textPath',
1066
- 'title',
1067
- 'tspan',
1068
- 'use',
1069
- 'view'
1070
- ].join('|')})$`, 'i');
1071
- this.xlinkNameSpace = 'http://www.w3.org/1999/xlink';
1072
- this.possibleXlinkNames = {
1073
- xlinkActuate: 'xlink:actuate',
1074
- xlinkactuate: 'xlink:actuate',
1075
- 'xlink:actuate': 'xlink:actuate',
1076
- xlinkArcrole: 'xlink:arcrole',
1077
- xlinkarcrole: 'xlink:arcrole',
1078
- 'xlink:arcrole': 'xlink:arcrole',
1079
- xlinkHref: 'xlink:href',
1080
- xlinkhref: 'xlink:href',
1081
- 'xlink:href': 'xlink:href',
1082
- xlinkRole: 'xlink:role',
1083
- xlinkrole: 'xlink:role',
1084
- 'xlink:role': 'xlink:role',
1085
- xlinkShow: 'xlink:show',
1086
- xlinkshow: 'xlink:show',
1087
- 'xlink:show': 'xlink:show',
1088
- xlinkTitle: 'xlink:title',
1089
- xlinktitle: 'xlink:title',
1090
- 'xlink:title': 'xlink:title',
1091
- xlinkType: 'xlink:type',
1092
- xlinktype: 'xlink:type',
1093
- 'xlink:type': 'xlink:type'
1094
- };
1095
- this.booleanProps = {
1096
- input: ['disabled', 'readonly'],
1097
- select: ['disabled', 'readonly'],
1098
- option: ['disabled', 'selected'],
1099
- button: ['disabled'],
1100
- video: ['controls', 'autoplay', 'loop', 'muted'],
1101
- audio: ['controls', 'autoplay', 'loop', 'muted'],
1102
- };
1103
- this.valueProps = {
1104
- input: ['value'],
1105
- option: ['value'],
1106
- video: ['src'],
1107
- audio: ['src']
1108
- };
1109
- }
1110
- listen(node, type, callback) {
1111
- node.addEventListener(type, callback);
1112
- }
1113
- unListen(node, type, callback) {
1114
- node.removeEventListener(type, callback);
1115
- }
1116
- createTextNode(textContent) {
1117
- return document.createTextNode(DomRenderer_1.replaceEmpty(textContent));
1118
- }
1119
- createElement(name) {
1120
- if (this.isSVG.test(name)) {
1121
- return document.createElementNS('http://www.w3.org/2000/svg', name);
1122
- }
1123
- return document.createElement(name);
1124
- }
1125
- appendChild(parent, newChild) {
1126
- parent.appendChild(newChild);
1127
- }
1128
- remove(node) {
1129
- var _a;
1130
- (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
1131
- }
1132
- insertBefore(newNode, ref) {
1133
- ref.parentNode.insertBefore(newNode, ref);
1134
- }
1135
- getChildByIndex(parent, index) {
1136
- return parent.childNodes[index] || null;
1137
- }
1138
- addClass(target, name) {
1139
- target.classList.add(name);
1140
- }
1141
- removeClass(target, name) {
1142
- target.classList.remove(name);
1143
- }
1144
- setStyle(target, key, value) {
1145
- target.style[key] = value !== null && value !== void 0 ? value : '';
1146
- }
1147
- syncTextContent(target, content) {
1148
- const c = DomRenderer_1.replaceEmpty(content);
1149
- if (target.textContent !== c) {
1150
- target.textContent = c;
1151
- }
1152
- }
1153
- removeStyle(target, key) {
1154
- target.style[key] = '';
1155
- }
1156
- setAttribute(target, key, value) {
1157
- if (this.possibleXlinkNames[key]) {
1158
- this.setXlinkAttribute(target, this.possibleXlinkNames[key], value);
1159
- return;
1160
- }
1161
- target.setAttribute(key, value);
1162
- const tag = target.tagName.toLowerCase();
1163
- const booleanTagNames = this.booleanProps[tag];
1164
- const valueTagNames = this.valueProps[tag];
1165
- if (booleanTagNames && booleanTagNames.includes(key)) {
1166
- target[key] = Boolean(value);
1167
- }
1168
- if (valueTagNames && valueTagNames.includes(key)) {
1169
- target[key] = value;
1170
- }
1171
- }
1172
- removeAttribute(target, key) {
1173
- if (this.possibleXlinkNames[key]) {
1174
- this.removeXlinkAttribute(target, this.possibleXlinkNames[key]);
1175
- }
1176
- target.removeAttribute(key);
1177
- const tag = target.tagName.toLowerCase();
1178
- const booleanTagNames = this.booleanProps[tag];
1179
- const valueTagNames = this.valueProps[tag];
1180
- if (booleanTagNames && booleanTagNames.includes(key)) {
1181
- target[key] = false;
1182
- }
1183
- if (valueTagNames && valueTagNames.includes(key)) {
1184
- target[key] = '';
1185
- }
1186
- }
1187
- setXlinkAttribute(target, key, value) {
1188
- target.setAttributeNS(this.xlinkNameSpace, key, value);
1189
- }
1190
- removeXlinkAttribute(target, key) {
1191
- target.removeAttributeNS(this.xlinkNameSpace, key);
1192
- }
1193
- replace(newChild, oldChild) {
1194
- oldChild.parentNode.replaceChild(newChild, oldChild);
1195
- }
1196
- copy() {
1197
- document.execCommand('copy');
1198
- }
1199
- static replaceEmpty(s) {
1200
- const empty = '\u00a0';
1201
- return s.replace(/\s\s+/g, str => {
1202
- return ' ' + Array.from({
1203
- length: str.length - 1
1204
- }).fill(empty).join('');
1205
- }).replace(/^\s|\s$/g, empty);
1206
- }
1207
- };
1208
- DomRenderer = DomRenderer_1 = __decorate([
1209
- Injectable()
1210
- ], DomRenderer);
1211
-
1212
- var Parser_1;
1213
- /**
1214
- * 用于解析 HTML,并把 HTML 内容转换为 Textbus 可以支持的组件或插槽数据
1215
- */
1216
- let Parser = Parser_1 = class Parser {
1217
- static parseHTML(html) {
1218
- return new DOMParser().parseFromString(html, 'text/html').body;
1219
- }
1220
- constructor(options, injector) {
1221
- var _a;
1222
- this.options = options;
1223
- this.injector = injector;
1224
- const componentLoaders = [
1225
- ...(options.componentLoaders || [])
1226
- ];
1227
- const formatLoaders = [
1228
- ...(options.formatLoaders || [])
1229
- ];
1230
- const attributeLoaders = [
1231
- ...(options.attributeLoaders || [])
1232
- ];
1233
- (_a = options.imports) === null || _a === void 0 ? void 0 : _a.forEach(i => {
1234
- componentLoaders.push(...(i.componentLoaders || []));
1235
- formatLoaders.push(...(i.formatLoaders || []));
1236
- });
1237
- this.componentLoaders = componentLoaders;
1238
- this.formatLoaders = formatLoaders;
1239
- this.attributeLoaders = attributeLoaders;
1240
- }
1241
- /**
1242
- * 使用指定的组件加载器解析一段 HTML 字符串
1243
- * @param html
1244
- * @param rootComponentLoader
1245
- */
1246
- parseDoc(html, rootComponentLoader) {
1247
- const element = Parser_1.parseHTML(html);
1248
- return rootComponentLoader.read(element, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
1249
- return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
1250
- });
1251
- }
1252
- /**
1253
- * 将一段 HTML 解析到指定插槽
1254
- * @param html
1255
- * @param rootSlot
1256
- */
1257
- parse(html, rootSlot) {
1258
- const element = Parser_1.parseHTML(html);
1259
- return this.readFormats(element, rootSlot);
1260
- }
1261
- readComponent(el, slot) {
1262
- if (el.nodeType === Node.ELEMENT_NODE) {
1263
- if (el.tagName === 'BR') {
1264
- slot.insert('\n');
1265
- return;
1266
- }
1267
- for (const t of this.componentLoaders) {
1268
- if (t.match(el)) {
1269
- const result = t.read(el, this.injector, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
1270
- return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
1271
- });
1272
- if (!result) {
1273
- return;
1274
- }
1275
- if (result instanceof Slot) {
1276
- result.toDelta().forEach(i => slot.insert(i.insert, i.formats));
1277
- return;
1278
- }
1279
- slot.insert(result);
1280
- return;
1281
- }
1282
- }
1283
- this.readFormats(el, slot);
1284
- }
1285
- else if (el.nodeType === Node.TEXT_NODE) {
1286
- this.readText(slot, el);
1287
- }
1288
- }
1289
- readText(slot, el) {
1290
- const textContent = el.textContent;
1291
- if (/^\s*[\r\n\u200b]+\s*$/.test(textContent)) {
1292
- return;
1293
- }
1294
- slot.insert(textContent);
1295
- }
1296
- readFormats(el, slot) {
1297
- const formats = this.formatLoaders.filter(f => {
1298
- return f.match(el);
1299
- }).map(f => {
1300
- return f.read(el);
1301
- });
1302
- const startIndex = slot.index;
1303
- let startNode = el.firstChild;
1304
- while (startNode) {
1305
- this.readComponent(startNode, slot);
1306
- startNode = startNode.nextSibling;
1307
- }
1308
- const endIndex = slot.index;
1309
- this.applyFormats(slot, formats.map(i => {
1310
- return {
1311
- formatter: i.formatter,
1312
- value: i.value,
1313
- startIndex,
1314
- endIndex
1315
- };
1316
- }));
1317
- slot.retain(endIndex);
1318
- return slot;
1319
- }
1320
- readSlot(childSlot, slotRootElement, slotContentElement) {
1321
- if (slotRootElement.nodeType === Node.ELEMENT_NODE) {
1322
- this.attributeLoaders.filter(a => {
1323
- return a.match(slotRootElement);
1324
- }).forEach(a => {
1325
- const r = a.read(slotRootElement);
1326
- childSlot.setAttribute(r.attribute, r.value);
1327
- });
1328
- }
1329
- if (slotContentElement.nodeType === Node.ELEMENT_NODE) {
1330
- this.readFormats(slotContentElement, childSlot);
1331
- }
1332
- else {
1333
- this.readText(childSlot, slotContentElement);
1334
- }
1335
- return childSlot;
1336
- }
1337
- applyFormats(slot, formatItems) {
1338
- slot.background(() => {
1339
- formatItems.forEach(i => {
1340
- slot.retain(i.startIndex);
1341
- slot.retain(i.endIndex - i.startIndex, i.formatter, i.value);
1342
- });
1343
- });
1344
- }
1345
- };
1346
- Parser = Parser_1 = __decorate([
1347
- Injectable(),
1348
- __param(0, Inject(EDITOR_OPTIONS)),
1349
- __metadata("design:paramtypes", [Object, Injector])
1350
- ], Parser);
1351
-
1352
- const iframeHTML = `
1353
- <!DOCTYPE html>
1354
- <html>
1355
- <head>
1356
- <meta charset="UTF-8">
1357
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
1358
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
1359
- <title>Textbus</title>
1360
- <style>
1361
- html {position: fixed; left:0; overflow: hidden}
1362
- html, body{height: 100%;width:100%}
1363
- body{margin:0; overflow: hidden}
1364
- textarea{width: 2000px;height: 100%;opacity: 0; padding: 0; outline: none; border: none; position: absolute; left:0; top:0;}
1365
- </style>
1366
- </head>
1367
- <body>
1368
- </body>
1369
- </html>
1370
- `;
1371
- class ExperimentalCaret {
1372
- get rect() {
1373
- return this.caret.getBoundingClientRect();
1374
- }
1375
- set display(v) {
1376
- this._display = v;
1377
- this.caret.style.visibility = v ? 'visible' : 'hidden';
1378
- }
1379
- get display() {
1380
- return this._display;
1381
- }
1382
- constructor(scheduler, editorMask) {
1383
- this.scheduler = scheduler;
1384
- this.editorMask = editorMask;
1385
- this.compositionState = null;
1386
- this.compositionElement = createElement('span', {
1387
- styles: {
1388
- textDecoration: 'underline'
1013
+ textDecoration: 'underline'
1389
1014
  }
1390
1015
  });
1391
1016
  this.timer = null;
@@ -1857,10 +1482,21 @@ let MagicInput = class MagicInput extends Input {
1857
1482
  textarea.value = '';
1858
1483
  return ev.data;
1859
1484
  }))).subscribe(text => {
1860
- var _a;
1861
1485
  this.composition = false;
1862
1486
  this.caret.compositionState = this.compositionState = null;
1863
- (_a = this.caret.compositionElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.caret.compositionElement);
1487
+ const compositionElement = this.caret.compositionElement;
1488
+ let nextSibling = compositionElement.nextSibling;
1489
+ while (nextSibling) {
1490
+ if (!nextSibling.textContent) {
1491
+ const next = nextSibling.nextSibling;
1492
+ nextSibling.remove();
1493
+ nextSibling = next;
1494
+ continue;
1495
+ }
1496
+ nextSibling.remove();
1497
+ break;
1498
+ }
1499
+ compositionElement.remove();
1864
1500
  if (text) {
1865
1501
  this.commander.write(text);
1866
1502
  }
@@ -1874,32 +1510,293 @@ let MagicInput = class MagicInput extends Input {
1874
1510
  isCompositionEnd = false;
1875
1511
  }));
1876
1512
  }
1877
- createEditableFrame() {
1878
- return createElement('iframe', {
1879
- attrs: {
1880
- scrolling: 'no'
1881
- },
1513
+ createEditableFrame() {
1514
+ return createElement('iframe', {
1515
+ attrs: {
1516
+ scrolling: 'no'
1517
+ },
1518
+ styles: {
1519
+ border: 'none',
1520
+ width: '100%',
1521
+ display: 'block',
1522
+ height: '100%',
1523
+ position: 'relative',
1524
+ top: this.isWindows ? '3px' : '0'
1525
+ }
1526
+ });
1527
+ }
1528
+ };
1529
+ MagicInput = __decorate([
1530
+ Injectable(),
1531
+ __metadata("design:paramtypes", [Parser,
1532
+ Keyboard,
1533
+ Commander,
1534
+ Selection,
1535
+ Controller,
1536
+ Scheduler,
1537
+ Injector])
1538
+ ], MagicInput);
1539
+
1540
+ /**
1541
+ * 远程光标绘制范围计算代理类,可用于定制特定场景下的远程选区绘制,如表格有选区,不会遵守常见的文档流形式
1542
+ */
1543
+ class CollaborateSelectionAwarenessDelegate {
1544
+ }
1545
+ /**
1546
+ * 协作光标绘制类
1547
+ */
1548
+ let CollaborateCursor = class CollaborateCursor {
1549
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
1550
+ this.nativeSelection = nativeSelection;
1551
+ this.scheduler = scheduler;
1552
+ this.selection = selection;
1553
+ this.awarenessDelegate = awarenessDelegate;
1554
+ this.host = createElement('div', {
1555
+ styles: {
1556
+ position: 'absolute',
1557
+ left: 0,
1558
+ top: 0,
1559
+ width: '100%',
1560
+ height: '100%',
1561
+ pointerEvents: 'none',
1562
+ zIndex: 1
1563
+ }
1564
+ });
1565
+ this.canvasContainer = createElement('div', {
1566
+ styles: {
1567
+ position: 'absolute',
1568
+ left: 0,
1569
+ top: 0,
1570
+ width: '100%',
1571
+ height: '100%',
1572
+ overflow: 'hidden'
1573
+ }
1574
+ });
1575
+ this.canvas = createElement('canvas', {
1576
+ styles: {
1577
+ position: 'absolute',
1578
+ opacity: 0.5,
1579
+ left: 0,
1580
+ top: 0,
1581
+ width: '100%',
1582
+ height: document.documentElement.clientHeight + 'px',
1583
+ pointerEvents: 'none',
1584
+ }
1585
+ });
1586
+ this.context = this.canvas.getContext('2d');
1587
+ this.tooltips = createElement('div', {
1588
+ styles: {
1589
+ position: 'absolute',
1590
+ left: 0,
1591
+ top: 0,
1592
+ width: '100%',
1593
+ height: '100%',
1594
+ pointerEvents: 'none',
1595
+ fontSize: '12px',
1596
+ zIndex: 10
1597
+ }
1598
+ });
1599
+ this.onRectsChange = new Subject();
1600
+ this.subscription = new Subscription();
1601
+ this.currentSelection = [];
1602
+ this.container = injector.get(VIEW_CONTAINER);
1603
+ this.canvasContainer.append(this.canvas);
1604
+ this.host.append(this.canvasContainer, this.tooltips);
1605
+ this.container.prepend(this.host);
1606
+ this.subscription.add(this.onRectsChange.subscribe(rects => {
1607
+ for (const rect of rects) {
1608
+ this.context.fillStyle = rect.color;
1609
+ this.context.beginPath();
1610
+ this.context.rect(rect.left, rect.top, rect.width, rect.height);
1611
+ this.context.fill();
1612
+ this.context.closePath();
1613
+ }
1614
+ }), fromEvent(window, 'resize').subscribe(() => {
1615
+ this.canvas.style.height = document.documentElement.clientHeight + 'px';
1616
+ this.refresh();
1617
+ }), this.scheduler.onDocChanged.subscribe(() => {
1618
+ this.refresh();
1619
+ }));
1620
+ }
1621
+ /**
1622
+ * 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
1623
+ */
1624
+ refresh() {
1625
+ this.draw(this.currentSelection);
1626
+ }
1627
+ destroy() {
1628
+ this.subscription.unsubscribe();
1629
+ }
1630
+ /**
1631
+ * 根据远程用户光标位置,绘制协作光标
1632
+ * @param paths
1633
+ */
1634
+ draw(paths) {
1635
+ this.currentSelection = paths;
1636
+ const containerRect = this.container.getBoundingClientRect();
1637
+ this.canvas.style.top = containerRect.top * -1 + 'px';
1638
+ this.canvas.width = this.canvas.offsetWidth;
1639
+ this.canvas.height = this.canvas.offsetHeight;
1640
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1641
+ const users = [];
1642
+ paths.filter(i => {
1643
+ return i.paths.anchor.length && i.paths.focus.length;
1644
+ }).forEach(item => {
1645
+ const anchorPaths = [...item.paths.anchor];
1646
+ const focusPaths = [...item.paths.focus];
1647
+ const anchorOffset = anchorPaths.pop();
1648
+ const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1649
+ const focusOffset = focusPaths.pop();
1650
+ const focusSlot = this.selection.findSlotByPaths(focusPaths);
1651
+ if (!anchorSlot || !focusSlot) {
1652
+ return;
1653
+ }
1654
+ const { focus, anchor } = this.nativeSelection.getPositionByRange({
1655
+ focusOffset,
1656
+ anchorOffset,
1657
+ focusSlot,
1658
+ anchorSlot
1659
+ });
1660
+ if (!focus || !anchor) {
1661
+ return;
1662
+ }
1663
+ const nativeRange = document.createRange();
1664
+ nativeRange.setStart(anchor.node, anchor.offset);
1665
+ nativeRange.setEnd(focus.node, focus.offset);
1666
+ if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1667
+ nativeRange.setStart(focus.node, focus.offset);
1668
+ nativeRange.setEnd(anchor.node, anchor.offset);
1669
+ }
1670
+ let rects = false;
1671
+ if (this.awarenessDelegate) {
1672
+ rects = this.awarenessDelegate.getRects({
1673
+ focusOffset,
1674
+ anchorOffset,
1675
+ focusSlot,
1676
+ anchorSlot
1677
+ }, nativeRange);
1678
+ }
1679
+ if (!rects) {
1680
+ rects = nativeRange.getClientRects();
1681
+ }
1682
+ const selectionRects = [];
1683
+ for (let i = rects.length - 1; i >= 0; i--) {
1684
+ const rect = rects[i];
1685
+ selectionRects.push({
1686
+ id: item.id,
1687
+ color: item.color,
1688
+ username: item.username,
1689
+ left: rect.left - containerRect.left,
1690
+ top: rect.top,
1691
+ width: rect.width,
1692
+ height: rect.height,
1693
+ });
1694
+ }
1695
+ this.onRectsChange.next(selectionRects);
1696
+ const cursorRange = nativeRange.cloneRange();
1697
+ cursorRange.setStart(focus.node, focus.offset);
1698
+ cursorRange.collapse(true);
1699
+ const cursorRect = getLayoutRectByRange(cursorRange);
1700
+ const rect = {
1701
+ id: item.id,
1702
+ username: item.username,
1703
+ color: item.color,
1704
+ left: cursorRect.left - containerRect.left,
1705
+ top: cursorRect.top - containerRect.top,
1706
+ width: 1,
1707
+ height: cursorRect.height
1708
+ };
1709
+ if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
1710
+ return;
1711
+ }
1712
+ users.push(rect);
1713
+ });
1714
+ this.drawUserCursor(users);
1715
+ }
1716
+ drawUserCursor(rects) {
1717
+ for (let i = 0; i < rects.length; i++) {
1718
+ const rect = rects[i];
1719
+ const { cursor, userTip, anchor } = this.getUserCursor(i);
1720
+ Object.assign(cursor.style, {
1721
+ left: rect.left + 'px',
1722
+ top: rect.top + 'px',
1723
+ width: rect.width + 'px',
1724
+ height: rect.height + 'px',
1725
+ background: rect.color,
1726
+ display: 'block'
1727
+ });
1728
+ anchor.style.background = rect.color;
1729
+ userTip.innerText = rect.username;
1730
+ userTip.style.background = rect.color;
1731
+ }
1732
+ for (let i = rects.length; i < this.tooltips.children.length; i++) {
1733
+ this.tooltips.removeChild(this.tooltips.children[i]);
1734
+ }
1735
+ }
1736
+ getUserCursor(index) {
1737
+ let child = this.tooltips.children[index];
1738
+ if (child) {
1739
+ const anchor = child.children[0];
1740
+ return {
1741
+ cursor: child,
1742
+ anchor,
1743
+ userTip: anchor.children[0]
1744
+ };
1745
+ }
1746
+ const userTip = createElement('span', {
1882
1747
  styles: {
1883
- border: 'none',
1884
- width: '100%',
1885
- display: 'block',
1886
- height: '100%',
1887
- position: 'relative',
1888
- top: this.isWindows ? '3px' : '0'
1748
+ position: 'absolute',
1749
+ left: '50%',
1750
+ transform: 'translateX(-50%)',
1751
+ marginBottom: '2px',
1752
+ bottom: '100%',
1753
+ whiteSpace: 'nowrap',
1754
+ color: '#fff',
1755
+ boxShadow: '0 1px 2px rgba(0,0,0,.1)',
1756
+ opacity: 0.8,
1757
+ borderRadius: '3px',
1758
+ padding: '3px 5px',
1759
+ pointerEvents: 'none',
1889
1760
  }
1890
1761
  });
1762
+ const anchor = createElement('span', {
1763
+ styles: {
1764
+ position: 'absolute',
1765
+ top: '-2px',
1766
+ left: '-2px',
1767
+ width: '5px',
1768
+ height: '5px',
1769
+ borderRadius: '50%',
1770
+ pointerEvents: 'auto',
1771
+ pointer: 'cursor',
1772
+ },
1773
+ children: [userTip]
1774
+ });
1775
+ child = createElement('span', {
1776
+ styles: {
1777
+ position: 'absolute',
1778
+ },
1779
+ children: [
1780
+ anchor
1781
+ ]
1782
+ });
1783
+ this.tooltips.append(child);
1784
+ return {
1785
+ cursor: child,
1786
+ anchor,
1787
+ userTip
1788
+ };
1891
1789
  }
1892
1790
  };
1893
- MagicInput = __decorate([
1791
+ CollaborateCursor = __decorate([
1894
1792
  Injectable(),
1895
- __metadata("design:paramtypes", [Parser,
1896
- Keyboard,
1897
- Commander,
1898
- Selection,
1899
- Controller,
1793
+ __param(4, Optional()),
1794
+ __metadata("design:paramtypes", [Injector,
1795
+ SelectionBridge,
1900
1796
  Scheduler,
1901
- Injector])
1902
- ], MagicInput);
1797
+ Selection,
1798
+ CollaborateSelectionAwarenessDelegate])
1799
+ ], CollaborateCursor);
1903
1800
 
1904
1801
  class NativeCaret {
1905
1802
  set nativeRange(range) {
@@ -2015,14 +1912,13 @@ let NativeInput = class NativeInput extends Input {
2015
1912
  get disabled() {
2016
1913
  return this._disabled;
2017
1914
  }
2018
- constructor(injector, parser, scheduler, selection, keyboard, renderer, commander, controller) {
1915
+ constructor(injector, parser, scheduler, selection, keyboard, domAdapter, commander, controller) {
2019
1916
  super();
2020
- this.injector = injector;
2021
1917
  this.parser = parser;
2022
1918
  this.scheduler = scheduler;
2023
1919
  this.selection = selection;
2024
1920
  this.keyboard = keyboard;
2025
- this.renderer = renderer;
1921
+ this.domAdapter = domAdapter;
2026
1922
  this.commander = commander;
2027
1923
  this.controller = controller;
2028
1924
  this.caret = new NativeCaret(this.scheduler);
@@ -2250,7 +2146,7 @@ let NativeInput = class NativeInput extends Input {
2250
2146
  if (!range) {
2251
2147
  break;
2252
2148
  }
2253
- const location = this.renderer.getLocationByNativeNode(range.startContainer);
2149
+ const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
2254
2150
  const startSlot = this.selection.startSlot;
2255
2151
  if (startSlot) {
2256
2152
  this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
@@ -2261,7 +2157,7 @@ let NativeInput = class NativeInput extends Input {
2261
2157
  case 'insertReplacementText': {
2262
2158
  this.composition = false;
2263
2159
  const range = ev.getTargetRanges()[0];
2264
- const location = this.renderer.getLocationByNativeNode(range.startContainer);
2160
+ const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
2265
2161
  const startSlot = this.selection.startSlot;
2266
2162
  this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
2267
2163
  this.commander.delete();
@@ -2315,7 +2211,7 @@ let NativeInput = class NativeInput extends Input {
2315
2211
  }
2316
2212
  if (ev.inputType === 'insertReplacementText') {
2317
2213
  const range = ev.getTargetRanges()[0];
2318
- const location = this.renderer.getLocationByNativeNode(range.startContainer);
2214
+ const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
2319
2215
  const startSlot = this.selection.startSlot;
2320
2216
  this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
2321
2217
  this.commander.delete();
@@ -2373,561 +2269,59 @@ NativeInput = __decorate([
2373
2269
  Scheduler,
2374
2270
  Selection,
2375
2271
  Keyboard,
2376
- Renderer,
2272
+ DomAdapter,
2377
2273
  Commander,
2378
2274
  Controller])
2379
2275
  ], NativeInput);
2380
2276
 
2381
- var OutputTranslator_1;
2382
- /**
2383
- * HTML 输出转换器,用于将虚拟 DOM 转换为 HTML 字符串
2384
- */
2385
- let OutputTranslator = OutputTranslator_1 = class OutputTranslator {
2386
- constructor() {
2387
- this.singleTagTest = new RegExp(`^(${OutputTranslator_1.singleTags.join('|')})$`, 'i');
2388
- }
2389
- /**
2390
- * 将虚拟 DOM 转换为 HTML 字符串的方法
2391
- * @param vDom 虚拟 DOM 节点
2392
- */
2393
- transform(vDom) {
2394
- return vDom.children.map(child => {
2395
- return this.vDomToHTMLString(child);
2396
- }).join('');
2397
- }
2398
- vDomToHTMLString(vDom) {
2399
- const xssFilter = OutputTranslator_1.simpleXSSFilter;
2400
- if (vDom instanceof VTextNode) {
2401
- return this.replaceEmpty(xssFilter.text(vDom.textContent), '&nbsp;');
2402
- }
2403
- const styles = Array.from(vDom.styles.keys()).filter(key => {
2404
- const v = vDom.styles.get(key);
2405
- return !(v === undefined || v === null || v === '');
2406
- }).map(key => {
2407
- const k = key.replace(/(?=[A-Z])/g, '-').toLowerCase();
2408
- return xssFilter.attrValue(`${k}:${vDom.styles.get(key)}`);
2409
- }).join(';');
2410
- const attrs = Array.from(vDom.attrs.keys()).filter(key => key !== 'ref' && vDom.attrs.get(key) !== false).map(k => {
2411
- const key = xssFilter.attrName(k);
2412
- const value = vDom.attrs.get(k);
2413
- return (value === true ? `${key}` : `${key}="${xssFilter.attrValue(`${value}`)}"`);
2414
- });
2415
- if (styles) {
2416
- attrs.push(`style="${styles}"`);
2417
- }
2418
- if (vDom.classes && vDom.classes.size) {
2419
- attrs.push(`class="${xssFilter.attrValue(Array.from(vDom.classes).join(' '))}"`);
2420
- }
2421
- let attrStr = attrs.join(' ');
2422
- attrStr = attrStr ? ' ' + attrStr : '';
2423
- if (this.singleTagTest.test(vDom.tagName)) {
2424
- return `<${vDom.tagName}${attrStr}>`;
2425
- }
2426
- const childHTML = vDom.children.map(child => {
2427
- return this.vDomToHTMLString(child);
2428
- }).join('');
2429
- return [
2430
- `<${vDom.tagName}${attrStr}>`,
2431
- childHTML,
2432
- `</${vDom.tagName}>`
2433
- ].join('');
2434
- }
2435
- replaceEmpty(s, target) {
2436
- return s.replace(/\s\s+/g, str => {
2437
- return ' ' + Array.from({
2438
- length: str.length - 1
2439
- }).fill(target).join('');
2440
- }).replace(/^\s|\s$/g, target);
2441
- }
2442
- };
2443
- OutputTranslator.singleTags = 'br,img,hr'.split(',');
2444
- OutputTranslator.simpleXSSFilter = {
2445
- text(text) {
2446
- return text.replace(/[><&]/g, str => {
2447
- return {
2448
- '<': '&lt;',
2449
- '>': '&gt;',
2450
- '&': '&amp;'
2451
- }[str];
2452
- });
2453
- },
2454
- attrName(text) {
2455
- return text.replace(/[><"'&]/g, str => {
2456
- return {
2457
- '<': '&lt;',
2458
- '>': '&gt;',
2459
- '"': '&quot;',
2460
- '\'': '&#x27;',
2461
- '&': '&amp;'
2462
- }[str];
2463
- });
2464
- },
2465
- attrValue(text) {
2466
- return text.replace(/["']/g, str => {
2467
- return {
2468
- '"': '&quot;',
2469
- '\'': '&#x27;'
2470
- }[str];
2471
- });
2472
- }
2473
- };
2474
- OutputTranslator = OutputTranslator_1 = __decorate([
2475
- Injectable()
2476
- ], OutputTranslator);
2477
-
2478
- const editorError = makeError('CoreEditor');
2479
- /**
2480
- * Textbus PC 端编辑器
2481
- */
2482
- class Viewer extends Starter {
2483
- get readonly() {
2484
- return this.controller.readonly;
2485
- }
2486
- set readonly(b) {
2487
- this.controller.readonly = b;
2488
- }
2489
- isFocus() {
2490
- return this._isFocus;
2491
- }
2492
- constructor(rootComponent, rootComponentLoader, options = {}) {
2493
- const id = 'textbus-' + Number((Math.random() + '').substring(2)).toString(16);
2494
- const { doc, mask, wrapper } = Viewer.createLayout(id, options.minHeight);
2495
- const staticProviders = [{
2277
+ class BrowserModule {
2278
+ constructor(host, config) {
2279
+ this.host = host;
2280
+ this.config = config;
2281
+ const { mask, wrapper } = BrowserModule.createLayout();
2282
+ wrapper.prepend(config.adapter.host);
2283
+ if (config.minHeight) {
2284
+ config.adapter.host.style.minHeight = config.minHeight;
2285
+ }
2286
+ this.providers = [{
2496
2287
  provide: EDITOR_OPTIONS,
2497
- useValue: options
2288
+ useValue: config
2498
2289
  }, {
2499
2290
  provide: VIEW_CONTAINER,
2500
2291
  useValue: wrapper
2501
2292
  }, {
2502
2293
  provide: VIEW_DOCUMENT,
2503
- useValue: doc
2294
+ useValue: config.adapter.host
2504
2295
  }, {
2505
2296
  provide: VIEW_MASK,
2506
2297
  useValue: mask
2507
- }, {
2508
- provide: NativeRenderer,
2509
- useExisting: DomRenderer
2510
2298
  }, {
2511
2299
  provide: NativeSelectionBridge,
2512
2300
  useExisting: SelectionBridge
2513
2301
  }, {
2514
2302
  provide: Input,
2515
- useClass: options.useContentEditable ? NativeInput : MagicInput
2303
+ useClass: config.useContentEditable ? NativeInput : MagicInput
2516
2304
  }, {
2517
- provide: Viewer,
2518
- useFactory: () => this
2519
- }];
2520
- super(Object.assign(Object.assign({}, options), { plugins: options.plugins || [], providers: [
2521
- ...(options.providers || []),
2522
- ...staticProviders,
2523
- DomRenderer,
2524
- Parser,
2525
- SelectionBridge,
2526
- OutputTranslator,
2527
- CollaborateCursor
2528
- ], setup: options.setup }));
2529
- this.rootComponent = rootComponent;
2530
- this.rootComponentLoader = rootComponentLoader;
2531
- this.options = options;
2532
- /** 编辑器是否已销毁 */
2533
- this.destroyed = false;
2534
- /** 编辑器是否已准备好 */
2535
- this.isReady = false;
2536
- this.changeEvent = new Subject();
2537
- this.subs = [];
2538
- this._isFocus = false;
2539
- this.resourceNodes = [];
2540
- this.focusEvent = new Subject();
2541
- this.blurEvent = new Subject();
2542
- this.saveEvent = new Subject();
2543
- this.styleSheet = '';
2544
- this.scripts = [];
2545
- this.links = [];
2546
- this.id = id;
2547
- this.workbench = wrapper;
2548
- this.onChange = this.changeEvent.asObservable();
2549
- this.onFocus = this.focusEvent.asObservable();
2550
- this.onBlur = this.blurEvent.asObservable();
2551
- this.onSave = this.saveEvent.asObservable();
2552
- this.controller = this.get(Controller);
2553
- }
2554
- /**
2555
- * 初始化编辑器
2556
- * @param host 编辑器容器
2557
- */
2558
- mount(host) {
2559
- const _super = Object.create(null, {
2560
- mount: { get: () => super.mount }
2561
- });
2562
- return __awaiter(this, void 0, void 0, function* () {
2563
- if (this.destroyed) {
2564
- throw editorError('the editor instance is destroyed!');
2565
- }
2566
- if (this.destroyed) {
2567
- return this;
2568
- }
2569
- const parser = this.get(Parser);
2570
- const registry = this.get(Registry);
2571
- const doc = this.get(VIEW_DOCUMENT);
2572
- this.initDefaultShortcut();
2573
- let component;
2574
- const content = this.options.content;
2575
- if (content) {
2576
- if (typeof content === 'string') {
2577
- component = parser.parseDoc(content, this.rootComponentLoader);
2578
- }
2579
- else {
2580
- component = registry.createComponentByFactory(content, this.rootComponent);
2581
- }
2582
- }
2583
- else {
2584
- component = this.rootComponent.createInstance(this);
2585
- }
2586
- this.initDocStyleSheetsAndScripts(this.options);
2587
- host.appendChild(this.workbench);
2588
- yield _super.mount.call(this, doc, component);
2589
- const renderer = this.get(Renderer);
2590
- const input = this.get(Input);
2591
- this.subs.push(renderer.onViewUpdated.subscribe(() => {
2592
- this.changeEvent.next();
2593
- }), input.caret.onPositionChange.pipe(map(p => !!p), distinctUntilChanged()).subscribe(b => {
2594
- if (b) {
2595
- this._isFocus = true;
2596
- this.focusEvent.next();
2597
- }
2598
- else {
2599
- this._isFocus = false;
2600
- this.blurEvent.next();
2601
- }
2602
- }));
2603
- this.isReady = true;
2604
- if (this.options.autoFocus) {
2605
- input.onReady.then(() => {
2606
- this.focus();
2607
- });
2608
- }
2609
- return this;
2610
- });
2611
- }
2612
- /**
2613
- * 获取焦点
2614
- */
2615
- focus() {
2616
- this.guardReady();
2617
- const selection = this.get(Selection);
2618
- const rootComponentRef = this.get(RootComponentRef);
2619
- if (selection.commonAncestorSlot) {
2620
- selection.restore();
2621
- return;
2622
- }
2623
- const location = selection.findFirstPosition(rootComponentRef.component.slots.get(0));
2624
- selection.setPosition(location.slot, location.offset);
2625
- selection.restore();
2626
- }
2627
- /**
2628
- * 取消编辑器焦点
2629
- */
2630
- blur() {
2631
- if (this.isReady) {
2632
- const selection = this.get(Selection);
2633
- selection.unSelect();
2634
- selection.restore();
2635
- }
2636
- }
2637
- /**
2638
- * 获取编辑器所有资源
2639
- */
2640
- getResources() {
2641
- var _a;
2642
- return {
2643
- styleSheets: ((_a = this.options) === null || _a === void 0 ? void 0 : _a.styleSheets) || [],
2644
- styleSheet: this.styleSheet,
2645
- links: this.links,
2646
- scripts: this.scripts
2647
- };
2648
- }
2649
- /**
2650
- * 获取 HTML 格式的内容
2651
- */
2652
- getHTML() {
2653
- this.guardReady();
2654
- const outputRenderer = this.get(OutputRenderer);
2655
- const outputTranslator = this.get(OutputTranslator);
2656
- const vDom = outputRenderer.render();
2657
- return outputTranslator.transform(vDom);
2658
- }
2659
- /**
2660
- * 获取 JSON 格式的内容
2661
- */
2662
- getJSON() {
2663
- this.guardReady();
2664
- const rootComponentRef = this.get(RootComponentRef);
2665
- return rootComponentRef.component.toJSON();
2666
- }
2667
- /**
2668
- * 清空内容
2669
- */
2670
- clear() {
2671
- this.replaceContent('');
2672
- const history = this.get(History);
2673
- history.clear();
2674
- }
2675
- /**
2676
- * 销毁编辑器
2677
- */
2678
- destroy() {
2679
- var _a;
2680
- if (this.destroyed) {
2681
- return;
2682
- }
2683
- this.destroyed = true;
2684
- this.subs.forEach(i => i.unsubscribe());
2685
- const types = [
2686
- Input
2687
- ];
2688
- types.forEach(i => {
2689
- this.get(i).destroy();
2690
- });
2691
- super.destroy();
2692
- (_a = this.workbench.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.workbench);
2693
- this.resourceNodes.forEach(node => {
2694
- var _a;
2695
- (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
2696
- });
2697
- }
2698
- /**
2699
- * 替换编辑的内容
2700
- * @param content
2701
- */
2702
- replaceContent(content) {
2703
- this.guardReady();
2704
- const parser = this.get(Parser);
2705
- const registry = this.get(Registry);
2706
- const rootComponentRef = this.get(RootComponentRef);
2707
- const selection = this.get(Selection);
2708
- const rootComponentLoader = this.rootComponentLoader;
2709
- let component;
2710
- if (typeof content === 'string') {
2711
- component = parser.parseDoc(content, rootComponentLoader);
2712
- }
2713
- else {
2714
- component = registry.createComponentByFactory(content, this.rootComponent);
2715
- }
2716
- selection.unSelect();
2717
- rootComponentRef.component.slots.clean();
2718
- rootComponentRef.component.slots.push(...component.slots.toArray());
2719
- invokeListener(component, 'onDestroy');
2720
- }
2721
- guardReady() {
2722
- if (this.destroyed) {
2723
- throw editorError('the editor instance is destroyed!');
2724
- }
2725
- if (!this.isReady) {
2726
- throw editorError('please wait for the editor to initialize before getting the content!');
2727
- }
2728
- }
2729
- initDefaultShortcut() {
2730
- const selection = this.get(Selection);
2731
- const keyboard = this.get(Keyboard);
2732
- const history = this.get(History);
2733
- const commander = this.get(Commander);
2734
- keyboard.addShortcut({
2735
- keymap: {
2736
- key: 's',
2737
- ctrlKey: true
2738
- },
2739
- action: () => {
2740
- this.saveEvent.next();
2741
- }
2742
- });
2743
- keyboard.addShortcut({
2744
- keymap: {
2745
- key: 'Enter'
2746
- },
2747
- action: () => {
2748
- commander.break();
2749
- }
2750
- });
2751
- keyboard.addShortcut({
2752
- keymap: {
2753
- key: 'Enter',
2754
- shiftKey: true
2755
- },
2756
- action: () => {
2757
- const startOffset = selection.startOffset;
2758
- const startSlot = selection.startSlot;
2759
- const isToEnd = startOffset === startSlot.length || startSlot.isEmpty;
2760
- const content = isToEnd ? '\n\n' : '\n';
2761
- const isInserted = commander.insert(content);
2762
- if (isInserted && isToEnd) {
2763
- selection.setPosition(startSlot, startOffset + 1);
2764
- }
2765
- }
2766
- });
2767
- keyboard.addShortcut({
2768
- keymap: {
2769
- key: ['Delete', 'Backspace']
2770
- },
2771
- action: (key) => {
2772
- commander.delete(key === 'Backspace');
2773
- }
2774
- });
2775
- keyboard.addShortcut({
2776
- keymap: {
2777
- key: ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']
2778
- },
2779
- action: (key) => {
2780
- switch (key) {
2781
- case 'ArrowLeft':
2782
- selection.toPrevious();
2783
- break;
2784
- case 'ArrowRight':
2785
- selection.toNext();
2786
- break;
2787
- case 'ArrowUp':
2788
- selection.toPreviousLine();
2789
- break;
2790
- case 'ArrowDown':
2791
- selection.toNextLine();
2792
- break;
2793
- }
2794
- }
2795
- });
2796
- keyboard.addShortcut({
2797
- keymap: {
2798
- key: ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'],
2799
- shiftKey: true
2800
- },
2801
- action: (key) => {
2802
- switch (key) {
2803
- case 'ArrowLeft':
2804
- selection.wrapToBefore();
2805
- break;
2806
- case 'ArrowRight':
2807
- selection.wrapToAfter();
2808
- break;
2809
- case 'ArrowUp':
2810
- selection.wrapToPreviousLine();
2811
- break;
2812
- case 'ArrowDown':
2813
- selection.wrapToNextLine();
2814
- break;
2815
- }
2816
- }
2817
- });
2818
- keyboard.addShortcut({
2819
- keymap: {
2820
- key: 'Tab'
2821
- },
2822
- action: () => {
2823
- commander.insert(' ');
2824
- }
2825
- });
2826
- keyboard.addShortcut({
2827
- keymap: {
2828
- key: 'a',
2829
- ctrlKey: true
2830
- },
2831
- action: () => {
2832
- selection.selectAll();
2833
- }
2834
- });
2835
- keyboard.addShortcut({
2836
- keymap: {
2837
- key: 'c',
2838
- ctrlKey: true
2839
- },
2840
- action: () => {
2841
- commander.copy();
2842
- }
2843
- });
2844
- keyboard.addShortcut({
2845
- keymap: {
2846
- key: 'x',
2847
- ctrlKey: true
2848
- },
2849
- action: () => {
2850
- commander.cut();
2851
- }
2852
- });
2853
- keyboard.addShortcut({
2854
- keymap: {
2855
- key: 'z',
2856
- ctrlKey: true
2857
- },
2858
- action: () => {
2859
- history.back();
2860
- }
2861
- });
2862
- keyboard.addShortcut({
2863
- keymap: {
2864
- key: 'z',
2865
- ctrlKey: true,
2866
- shiftKey: true
2305
+ provide: ViewAdapter,
2306
+ useFactory(v) {
2307
+ return v;
2308
+ },
2309
+ deps: [DomAdapter]
2310
+ }, {
2311
+ provide: DomAdapter,
2312
+ useValue: config.adapter
2867
2313
  },
2868
- action: () => {
2869
- history.forward();
2870
- }
2871
- });
2314
+ Parser,
2315
+ SelectionBridge,
2316
+ CollaborateCursor
2317
+ ];
2318
+ this.workbench = wrapper;
2319
+ this.host.append(wrapper);
2872
2320
  }
2873
- initDocStyleSheetsAndScripts(options) {
2874
- var _a;
2875
- const loaders = [];
2876
- (_a = options.imports) === null || _a === void 0 ? void 0 : _a.forEach(module => {
2877
- loaders.push(...(module.componentLoaders || []));
2878
- });
2879
- loaders.push(...(options.componentLoaders || []));
2880
- const resources = loaders.filter(i => i.resources).map(i => i.resources);
2881
- const docStyles = [];
2882
- const editModeStyles = [];
2883
- resources.forEach(metadata => {
2884
- var _a, _b;
2885
- if (Array.isArray(metadata.links)) {
2886
- this.links.push(...metadata.links);
2887
- }
2888
- docStyles.push(((_a = metadata.styles) === null || _a === void 0 ? void 0 : _a.join('')) || '');
2889
- editModeStyles.push(((_b = metadata.editModeStyles) === null || _b === void 0 ? void 0 : _b.join('')) || '');
2890
- });
2891
- this.links.forEach(link => {
2892
- const linkEle = document.createElement('link');
2893
- Object.assign(linkEle, link);
2894
- this.resourceNodes.push(linkEle);
2895
- document.head.appendChild(linkEle);
2896
- });
2897
- const styleEl = document.createElement('style');
2898
- docStyles.push(...(options.styleSheets || []));
2899
- editModeStyles.push(`#${this.id} *::selection{background-color: rgba(18, 150, 219, .2); color:inherit}`, ...(options.editingStyleSheets || []));
2900
- this.styleSheet = Viewer.cssMin(docStyles.join(''));
2901
- styleEl.innerHTML = this.styleSheet + Viewer.cssMin(editModeStyles.join(''));
2902
- this.resourceNodes.push(styleEl);
2903
- document.head.append(styleEl);
2904
- resources.filter(i => { var _a; return (_a = i.scripts) === null || _a === void 0 ? void 0 : _a.length; }).map(i => i.scripts).flat().forEach(src => {
2905
- if (src) {
2906
- const script = document.createElement('script');
2907
- script.src = src;
2908
- this.scripts.push(src);
2909
- document.head.appendChild(script);
2910
- this.resourceNodes.push(script);
2911
- }
2912
- });
2321
+ onDestroy() {
2322
+ this.workbench.remove();
2913
2323
  }
2914
- static createLayout(id, minHeight = '100%') {
2915
- const doc = createElement('div', {
2916
- styles: {
2917
- cursor: 'text',
2918
- wordBreak: 'break-all',
2919
- boxSizing: 'border-box',
2920
- minHeight,
2921
- flex: 1,
2922
- outline: 'none'
2923
- },
2924
- attrs: {
2925
- 'data-textbus-view': VIEW_DOCUMENT,
2926
- },
2927
- props: {
2928
- id
2929
- }
2930
- });
2324
+ static createLayout() {
2931
2325
  const mask = createElement('div', {
2932
2326
  attrs: {
2933
2327
  'data-textbus-view': VIEW_MASK,
@@ -2953,20 +2347,13 @@ class Viewer extends Starter {
2953
2347
  position: 'relative',
2954
2348
  flexDirection: 'column'
2955
2349
  },
2956
- children: [doc, mask]
2350
+ children: [mask]
2957
2351
  });
2958
2352
  return {
2959
2353
  wrapper,
2960
- doc,
2961
2354
  mask
2962
2355
  };
2963
2356
  }
2964
- static cssMin(str) {
2965
- return str
2966
- .replace(/\s*(?=[>{}:;,[])/g, '')
2967
- .replace(/([>{}:;,])\s*/g, '$1')
2968
- .replace(/;}/g, '}').replace(/\s+/, ' ').trim();
2969
- }
2970
2357
  }
2971
2358
 
2972
- export { CollaborateCursor, CollaborateSelectionAwarenessDelegate, DomRenderer, EDITOR_OPTIONS, Input, MagicInput, NativeInput, OutputTranslator, Parser, SelectionBridge, VIEW_CONTAINER, VIEW_DOCUMENT, VIEW_MASK, Viewer, createElement, createTextNode, getLayoutRectByRange, isFirefox, isMac, isMobileBrowser, isSafari, isWindows };
2359
+ export { BrowserModule, CollaborateCursor, CollaborateSelectionAwarenessDelegate, DomAdapter, EDITOR_OPTIONS, Input, MagicInput, Parser, SelectionBridge, VIEW_CONTAINER, VIEW_DOCUMENT, VIEW_MASK, createElement, createTextNode, getLayoutRectByRange, isFirefox, isMac, isMobileBrowser, isSafari, isWindows };