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

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, ComponentInstance, VElement, VTextNode, 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,313 @@ 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 端浏览器渲染能力桥接器抽象类,提供了 DOM 元素查询能力,具体渲染能力由各前端框架实现相应桥接
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 = createBidirectionalMapping(a => {
326
+ return a instanceof ComponentInstance;
327
+ });
328
+ this.slotRootNativeElementCaches = createBidirectionalMapping(a => {
329
+ return a instanceof Slot;
330
+ });
331
+ this.slotRootVElementCaches = new WeakMap();
332
+ }
333
+ render(rootComponent) {
334
+ const view = this.componentRender(rootComponent);
335
+ return this.mount(this.host, view);
336
+ }
337
+ copy() {
338
+ document.execCommand('copy');
339
+ }
340
+ /**
341
+ * 根据组件获取组件的根 DOM 节点
342
+ * @param component
343
+ */
344
+ getNativeNodeByComponent(component) {
345
+ return this.componentRootElementCaches.get(component) || null;
346
+ }
347
+ /**
348
+ * 根据 DOM 节点,获对对应的组件根节点,如传入的 DOM 节点不为组件的根节点,则返回 null
349
+ * @param node
350
+ */
351
+ getComponentByNativeNode(node) {
352
+ return this.componentRootElementCaches.get(node) || null;
353
+ }
354
+ /**
355
+ * 根据插槽获取插槽的根 DOM 节点
356
+ * @param slot
357
+ */
358
+ getNativeNodeBySlot(slot) {
359
+ return this.slotRootNativeElementCaches.get(slot) || null;
360
+ }
361
+ /**
362
+ * 根据 DOM 节点,获对对应的插槽根节点,如传入的 DOM 节点不为插槽的根节点,则返回 null
363
+ * @param node
364
+ */
365
+ getSlotByNativeNode(node) {
366
+ return this.slotRootNativeElementCaches.get(node) || null;
367
+ }
368
+ /**
369
+ * 获取插槽内容节点集合
370
+ * @param slot
371
+ */
372
+ getNodesBySlot(slot) {
373
+ const rootNativeNode = this.getNativeNodeBySlot(slot);
374
+ if (!rootNativeNode) {
375
+ return [];
376
+ }
377
+ const rootVNode = this.slotRootVElementCaches.get(slot);
378
+ const getNodes = (vElement, nativeNode, result) => {
379
+ if (vElement.location) {
380
+ result.push(nativeNode);
381
+ }
382
+ for (let i = 0; i < vElement.children.length; i++) {
383
+ const vChild = vElement.children[i];
384
+ const nativeChild = nativeNode.childNodes[i];
385
+ if (vChild instanceof VElement) {
386
+ getNodes(vChild, nativeChild, result);
387
+ }
388
+ else if (vChild instanceof VTextNode) {
389
+ result.push(nativeChild);
390
+ }
391
+ else {
392
+ result.push(this.getNativeNodeByComponent(vChild));
393
+ }
394
+ }
395
+ return result;
396
+ };
397
+ return getNodes(rootVNode, rootNativeNode, []);
398
+ }
399
+ /**
400
+ * 获取原生节点的原始数据在文档中的位置
401
+ * @param node
402
+ */
403
+ getLocationByNativeNode(node) {
404
+ let slotRootNode = node;
405
+ while (!this.slotRootNativeElementCaches.get(slotRootNode)) {
406
+ slotRootNode = slotRootNode.parentNode;
407
+ if (!slotRootNode) {
408
+ return null;
409
+ }
410
+ }
411
+ const slot = this.slotRootNativeElementCaches.get(slotRootNode);
412
+ const rootVNode = this.slotRootVElementCaches.get(slot);
413
+ const getLocation = (target, tree, vNodeTree) => {
414
+ if (target === tree) {
415
+ return Object.assign({}, vNodeTree.location);
416
+ }
417
+ const childNodes = tree.childNodes;
418
+ for (let i = 0; i < childNodes.length; i++) {
419
+ const child = vNodeTree.children[i];
420
+ const nativeChild = tree.childNodes[i];
421
+ if (nativeChild === target) {
422
+ if (child instanceof ComponentInstance) {
423
+ const index = child.parent.indexOf(child);
424
+ return {
425
+ slot: child.parent,
426
+ startIndex: index,
427
+ endIndex: index + 1
428
+ };
429
+ }
430
+ return child.location;
431
+ }
432
+ else if (child instanceof VElement) {
433
+ let r = null;
434
+ if (nativeChild.nodeType === Node.ELEMENT_NODE) {
435
+ r = getLocation(target, nativeChild, child);
436
+ }
437
+ if (r) {
438
+ return r;
439
+ }
440
+ }
441
+ }
442
+ return null;
443
+ };
444
+ return getLocation(node, slotRootNode, rootVNode);
445
+ }
446
+ }
447
+
167
448
  /**
168
449
  * Textbus PC 端选区桥接实现
169
450
  */
170
451
  let SelectionBridge = class SelectionBridge {
171
- constructor(config, injector, controller, selection, rootComponentRef, input, renderer) {
452
+ constructor(config, injector, controller, selection, rootComponentRef, input, domAdapter) {
172
453
  this.config = config;
173
- this.injector = injector;
174
- this.controller = controller;
175
454
  this.selection = selection;
176
455
  this.rootComponentRef = rootComponentRef;
177
456
  this.input = input;
178
- this.renderer = renderer;
457
+ this.domAdapter = domAdapter;
179
458
  this.nativeSelection = document.getSelection();
180
- this.selectionMaskElement = createElement('style');
181
459
  this.selectionChangeEvent = new Subject();
182
460
  this.subs = [];
183
461
  this.connector = null;
184
462
  this.ignoreSelectionChange = false;
185
463
  this.changeFromUser = false;
186
464
  this.docContainer = injector.get(VIEW_DOCUMENT);
187
- this.maskContainer = injector.get(VIEW_MASK);
188
465
  this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(filter(() => {
189
466
  return !controller.readonly;
190
467
  }));
191
- document.head.appendChild(this.selectionMaskElement);
192
468
  this.sub = this.onSelectionChange.subscribe((r) => {
193
469
  if (r) {
194
470
  input.focus(r, this.changeFromUser);
@@ -484,22 +760,14 @@ let SelectionBridge = class SelectionBridge {
484
760
  const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
485
761
  if (!this.docContainer.contains(selection.focusNode)) {
486
762
  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);
763
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.first);
492
764
  if (!nativeNode) {
493
765
  return;
494
766
  }
495
767
  nativeRange.setEndAfter(nativeNode.lastChild);
496
768
  }
497
769
  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);
770
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.last);
503
771
  if (!nativeNode) {
504
772
  return;
505
773
  }
@@ -556,30 +824,28 @@ let SelectionBridge = class SelectionBridge {
556
824
  }
557
825
  findSelectedNodeAndOffset(slot, offset) {
558
826
  const prev = slot.getContentAtIndex(offset - 1);
559
- const vNodes = this.renderer.getVNodesBySlot(slot);
827
+ const nodes = this.domAdapter.getNodesBySlot(slot);
560
828
  if (prev) {
561
829
  if (typeof prev !== 'string') {
562
- const vNode = this.renderer.getVNodeByComponent(prev);
563
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
830
+ const nativeNode = this.domAdapter.getNativeNodeByComponent(prev);
564
831
  return {
565
832
  node: nativeNode.parentNode,
566
833
  offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode) + 1
567
834
  };
568
835
  }
569
836
  else if (prev === '\n') {
570
- for (const vNode of vNodes) {
571
- if (vNode instanceof VTextNode) {
837
+ for (const node of nodes) {
838
+ if (node instanceof Text) {
572
839
  continue;
573
840
  }
574
- if (vNode.tagName === 'br') {
575
- const position = this.renderer.getLocationByVNode(vNode);
841
+ if (node.nodeName === 'BR') {
842
+ const position = this.domAdapter.getLocationByNativeNode(node);
576
843
  if (position) {
577
844
  if (position.endIndex === offset) {
578
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
579
- const parentNode = nativeNode.parentNode;
845
+ const parentNode = node.parentNode;
580
846
  return {
581
847
  node: parentNode,
582
- offset: Array.from(parentNode.childNodes).indexOf(nativeNode) + 1
848
+ offset: Array.from(parentNode.childNodes).indexOf(node) + 1
583
849
  };
584
850
  }
585
851
  }
@@ -589,36 +855,33 @@ let SelectionBridge = class SelectionBridge {
589
855
  }
590
856
  const current = slot.getContentAtIndex(offset);
591
857
  if (current && typeof current !== 'string') {
592
- const vNode = this.renderer.getVNodeByComponent(current);
593
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
858
+ const nativeNode = this.domAdapter.getNativeNodeByComponent(current);
594
859
  return {
595
860
  node: nativeNode.parentNode,
596
861
  offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
597
862
  };
598
863
  }
599
- for (const vNode of vNodes) {
600
- if (vNode instanceof VElement) {
601
- if (vNode.tagName === 'br') {
602
- const position = this.renderer.getLocationByVNode(vNode);
864
+ for (const node of nodes) {
865
+ if (node instanceof Element) {
866
+ if (node.tagName === 'BR') {
867
+ const position = this.domAdapter.getLocationByNativeNode(node);
603
868
  if (position) {
604
869
  if (position.startIndex === offset) {
605
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
606
- const parentNode = nativeNode.parentNode;
870
+ const parentNode = node.parentNode;
607
871
  return {
608
872
  node: parentNode,
609
- offset: Array.from(parentNode.childNodes).indexOf(nativeNode)
873
+ offset: Array.from(parentNode.childNodes).indexOf(node)
610
874
  };
611
875
  }
612
876
  }
613
877
  }
614
878
  continue;
615
879
  }
616
- const position = this.renderer.getLocationByVNode(vNode);
880
+ const position = this.domAdapter.getLocationByNativeNode(node);
617
881
  if (position) {
618
882
  if (offset >= position.startIndex && offset <= position.endIndex) {
619
- const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
620
883
  return {
621
- node: nativeNode,
884
+ node: node,
622
885
  offset: offset - position.startIndex
623
886
  };
624
887
  }
@@ -629,10 +892,10 @@ let SelectionBridge = class SelectionBridge {
629
892
  getCorrectedPosition(node, offset, toAfter, excludeNodes = []) {
630
893
  excludeNodes.push(node);
631
894
  if (node.nodeType === Node.ELEMENT_NODE) {
632
- const containerPosition = this.renderer.getLocationByNativeNode(node);
895
+ const containerPosition = this.domAdapter.getLocationByNativeNode(node);
633
896
  const childNode = node.childNodes[offset];
634
897
  if (childNode) {
635
- const childPosition = this.renderer.getLocationByNativeNode(childNode);
898
+ const childPosition = this.domAdapter.getLocationByNativeNode(childNode);
636
899
  if (childPosition) {
637
900
  if (containerPosition) {
638
901
  return {
@@ -646,7 +909,7 @@ let SelectionBridge = class SelectionBridge {
646
909
  }
647
910
  const prevNode = node.childNodes[offset - 1];
648
911
  if (prevNode) {
649
- const prevPosition = this.renderer.getLocationByNativeNode(prevNode);
912
+ const prevPosition = this.domAdapter.getLocationByNativeNode(prevNode);
650
913
  if (prevPosition && containerPosition) {
651
914
  return {
652
915
  slot: prevPosition.slot,
@@ -667,7 +930,7 @@ let SelectionBridge = class SelectionBridge {
667
930
  return this.findFocusNodeByParent(node, toAfter, excludeNodes);
668
931
  }
669
932
  else if (node.nodeType === Node.TEXT_NODE) {
670
- const containerPosition = this.renderer.getLocationByNativeNode(node);
933
+ const containerPosition = this.domAdapter.getLocationByNativeNode(node);
671
934
  if (containerPosition) {
672
935
  return {
673
936
  slot: containerPosition.slot,
@@ -691,7 +954,7 @@ let SelectionBridge = class SelectionBridge {
691
954
  return this.findFocusNodeByParent(node, toAfter, excludeNodes);
692
955
  }
693
956
  excludeNodes.push(node);
694
- const position = this.renderer.getLocationByNativeNode(node);
957
+ const position = this.domAdapter.getLocationByNativeNode(node);
695
958
  if (position) {
696
959
  return {
697
960
  slot: position.slot,
@@ -711,7 +974,7 @@ let SelectionBridge = class SelectionBridge {
711
974
  findFocusNodeByParent(node, toAfter, excludeNodes) {
712
975
  const parentNode = node.parentNode;
713
976
  if (parentNode) {
714
- const parentPosition = this.renderer.getLocationByNativeNode(parentNode);
977
+ const parentPosition = this.domAdapter.getLocationByNativeNode(parentNode);
715
978
  if (parentPosition) {
716
979
  return {
717
980
  slot: parentPosition.slot,
@@ -732,654 +995,40 @@ SelectionBridge = __decorate([
732
995
  Selection,
733
996
  RootComponentRef,
734
997
  Input,
735
- Renderer])
998
+ DomAdapter])
736
999
  ], SelectionBridge);
737
1000
 
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;
750
- this.scheduler = scheduler;
751
- this.selection = selection;
752
- this.awarenessDelegate = awarenessDelegate;
753
- this.host = createElement('div', {
754
- 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) {
1001
+ const iframeHTML = `
1002
+ <!DOCTYPE html>
1003
+ <html>
1004
+ <head>
1005
+ <meta charset="UTF-8">
1006
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
1007
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
1008
+ <title>Textbus</title>
1009
+ <style>
1010
+ html {position: fixed; left:0; overflow: hidden}
1011
+ html, body{height: 100%;width:100%}
1012
+ body{margin:0; overflow: hidden}
1013
+ textarea{width: 2000px;height: 100%;opacity: 0; padding: 0; outline: none; border: none; position: absolute; left:0; top:0;}
1014
+ </style>
1015
+ </head>
1016
+ <body>
1017
+ </body>
1018
+ </html>
1019
+ `;
1020
+ class ExperimentalCaret {
1021
+ get rect() {
1022
+ return this.caret.getBoundingClientRect();
1023
+ }
1024
+ set display(v) {
1025
+ this._display = v;
1026
+ this.caret.style.visibility = v ? 'visible' : 'hidden';
1027
+ }
1028
+ get display() {
1029
+ return this._display;
1030
+ }
1031
+ constructor(scheduler, editorMask) {
1383
1032
  this.scheduler = scheduler;
1384
1033
  this.editorMask = editorMask;
1385
1034
  this.compositionState = null;
@@ -1857,10 +1506,21 @@ let MagicInput = class MagicInput extends Input {
1857
1506
  textarea.value = '';
1858
1507
  return ev.data;
1859
1508
  }))).subscribe(text => {
1860
- var _a;
1861
1509
  this.composition = false;
1862
1510
  this.caret.compositionState = this.compositionState = null;
1863
- (_a = this.caret.compositionElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.caret.compositionElement);
1511
+ const compositionElement = this.caret.compositionElement;
1512
+ let nextSibling = compositionElement.nextSibling;
1513
+ while (nextSibling) {
1514
+ if (!nextSibling.textContent) {
1515
+ const next = nextSibling.nextSibling;
1516
+ nextSibling.remove();
1517
+ nextSibling = next;
1518
+ continue;
1519
+ }
1520
+ nextSibling.remove();
1521
+ break;
1522
+ }
1523
+ compositionElement.remove();
1864
1524
  if (text) {
1865
1525
  this.commander.write(text);
1866
1526
  }
@@ -1874,32 +1534,293 @@ let MagicInput = class MagicInput extends Input {
1874
1534
  isCompositionEnd = false;
1875
1535
  }));
1876
1536
  }
1877
- createEditableFrame() {
1878
- return createElement('iframe', {
1879
- attrs: {
1880
- scrolling: 'no'
1881
- },
1537
+ createEditableFrame() {
1538
+ return createElement('iframe', {
1539
+ attrs: {
1540
+ scrolling: 'no'
1541
+ },
1542
+ styles: {
1543
+ border: 'none',
1544
+ width: '100%',
1545
+ display: 'block',
1546
+ height: '100%',
1547
+ position: 'relative',
1548
+ top: this.isWindows ? '3px' : '0'
1549
+ }
1550
+ });
1551
+ }
1552
+ };
1553
+ MagicInput = __decorate([
1554
+ Injectable(),
1555
+ __metadata("design:paramtypes", [Parser,
1556
+ Keyboard,
1557
+ Commander,
1558
+ Selection,
1559
+ Controller,
1560
+ Scheduler,
1561
+ Injector])
1562
+ ], MagicInput);
1563
+
1564
+ /**
1565
+ * 远程光标绘制范围计算代理类,可用于定制特定场景下的远程选区绘制,如表格有选区,不会遵守常见的文档流形式
1566
+ */
1567
+ class CollaborateSelectionAwarenessDelegate {
1568
+ }
1569
+ /**
1570
+ * 协作光标绘制类
1571
+ */
1572
+ let CollaborateCursor = class CollaborateCursor {
1573
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
1574
+ this.nativeSelection = nativeSelection;
1575
+ this.scheduler = scheduler;
1576
+ this.selection = selection;
1577
+ this.awarenessDelegate = awarenessDelegate;
1578
+ this.host = createElement('div', {
1579
+ styles: {
1580
+ position: 'absolute',
1581
+ left: 0,
1582
+ top: 0,
1583
+ width: '100%',
1584
+ height: '100%',
1585
+ pointerEvents: 'none',
1586
+ zIndex: 1
1587
+ }
1588
+ });
1589
+ this.canvasContainer = createElement('div', {
1590
+ styles: {
1591
+ position: 'absolute',
1592
+ left: 0,
1593
+ top: 0,
1594
+ width: '100%',
1595
+ height: '100%',
1596
+ overflow: 'hidden'
1597
+ }
1598
+ });
1599
+ this.canvas = createElement('canvas', {
1600
+ styles: {
1601
+ position: 'absolute',
1602
+ opacity: 0.5,
1603
+ left: 0,
1604
+ top: 0,
1605
+ width: '100%',
1606
+ height: document.documentElement.clientHeight + 'px',
1607
+ pointerEvents: 'none',
1608
+ }
1609
+ });
1610
+ this.context = this.canvas.getContext('2d');
1611
+ this.tooltips = createElement('div', {
1612
+ styles: {
1613
+ position: 'absolute',
1614
+ left: 0,
1615
+ top: 0,
1616
+ width: '100%',
1617
+ height: '100%',
1618
+ pointerEvents: 'none',
1619
+ fontSize: '12px',
1620
+ zIndex: 10
1621
+ }
1622
+ });
1623
+ this.onRectsChange = new Subject();
1624
+ this.subscription = new Subscription();
1625
+ this.currentSelection = [];
1626
+ this.container = injector.get(VIEW_CONTAINER);
1627
+ this.canvasContainer.append(this.canvas);
1628
+ this.host.append(this.canvasContainer, this.tooltips);
1629
+ this.container.prepend(this.host);
1630
+ this.subscription.add(this.onRectsChange.subscribe(rects => {
1631
+ for (const rect of rects) {
1632
+ this.context.fillStyle = rect.color;
1633
+ this.context.beginPath();
1634
+ this.context.rect(rect.left, rect.top, rect.width, rect.height);
1635
+ this.context.fill();
1636
+ this.context.closePath();
1637
+ }
1638
+ }), fromEvent(window, 'resize').subscribe(() => {
1639
+ this.canvas.style.height = document.documentElement.clientHeight + 'px';
1640
+ this.refresh();
1641
+ }), this.scheduler.onDocChanged.subscribe(() => {
1642
+ this.refresh();
1643
+ }));
1644
+ }
1645
+ /**
1646
+ * 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
1647
+ */
1648
+ refresh() {
1649
+ this.draw(this.currentSelection);
1650
+ }
1651
+ destroy() {
1652
+ this.subscription.unsubscribe();
1653
+ }
1654
+ /**
1655
+ * 根据远程用户光标位置,绘制协作光标
1656
+ * @param paths
1657
+ */
1658
+ draw(paths) {
1659
+ this.currentSelection = paths;
1660
+ const containerRect = this.container.getBoundingClientRect();
1661
+ this.canvas.style.top = containerRect.top * -1 + 'px';
1662
+ this.canvas.width = this.canvas.offsetWidth;
1663
+ this.canvas.height = this.canvas.offsetHeight;
1664
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1665
+ const users = [];
1666
+ paths.filter(i => {
1667
+ return i.paths.anchor.length && i.paths.focus.length;
1668
+ }).forEach(item => {
1669
+ const anchorPaths = [...item.paths.anchor];
1670
+ const focusPaths = [...item.paths.focus];
1671
+ const anchorOffset = anchorPaths.pop();
1672
+ const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1673
+ const focusOffset = focusPaths.pop();
1674
+ const focusSlot = this.selection.findSlotByPaths(focusPaths);
1675
+ if (!anchorSlot || !focusSlot) {
1676
+ return;
1677
+ }
1678
+ const { focus, anchor } = this.nativeSelection.getPositionByRange({
1679
+ focusOffset,
1680
+ anchorOffset,
1681
+ focusSlot,
1682
+ anchorSlot
1683
+ });
1684
+ if (!focus || !anchor) {
1685
+ return;
1686
+ }
1687
+ const nativeRange = document.createRange();
1688
+ nativeRange.setStart(anchor.node, anchor.offset);
1689
+ nativeRange.setEnd(focus.node, focus.offset);
1690
+ if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1691
+ nativeRange.setStart(focus.node, focus.offset);
1692
+ nativeRange.setEnd(anchor.node, anchor.offset);
1693
+ }
1694
+ let rects = false;
1695
+ if (this.awarenessDelegate) {
1696
+ rects = this.awarenessDelegate.getRects({
1697
+ focusOffset,
1698
+ anchorOffset,
1699
+ focusSlot,
1700
+ anchorSlot
1701
+ }, nativeRange);
1702
+ }
1703
+ if (!rects) {
1704
+ rects = nativeRange.getClientRects();
1705
+ }
1706
+ const selectionRects = [];
1707
+ for (let i = rects.length - 1; i >= 0; i--) {
1708
+ const rect = rects[i];
1709
+ selectionRects.push({
1710
+ id: item.id,
1711
+ color: item.color,
1712
+ username: item.username,
1713
+ left: rect.left - containerRect.left,
1714
+ top: rect.top,
1715
+ width: rect.width,
1716
+ height: rect.height,
1717
+ });
1718
+ }
1719
+ this.onRectsChange.next(selectionRects);
1720
+ const cursorRange = nativeRange.cloneRange();
1721
+ cursorRange.setStart(focus.node, focus.offset);
1722
+ cursorRange.collapse(true);
1723
+ const cursorRect = getLayoutRectByRange(cursorRange);
1724
+ const rect = {
1725
+ id: item.id,
1726
+ username: item.username,
1727
+ color: item.color,
1728
+ left: cursorRect.left - containerRect.left,
1729
+ top: cursorRect.top - containerRect.top,
1730
+ width: 1,
1731
+ height: cursorRect.height
1732
+ };
1733
+ if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
1734
+ return;
1735
+ }
1736
+ users.push(rect);
1737
+ });
1738
+ this.drawUserCursor(users);
1739
+ }
1740
+ drawUserCursor(rects) {
1741
+ for (let i = 0; i < rects.length; i++) {
1742
+ const rect = rects[i];
1743
+ const { cursor, userTip, anchor } = this.getUserCursor(i);
1744
+ Object.assign(cursor.style, {
1745
+ left: rect.left + 'px',
1746
+ top: rect.top + 'px',
1747
+ width: rect.width + 'px',
1748
+ height: rect.height + 'px',
1749
+ background: rect.color,
1750
+ display: 'block'
1751
+ });
1752
+ anchor.style.background = rect.color;
1753
+ userTip.innerText = rect.username;
1754
+ userTip.style.background = rect.color;
1755
+ }
1756
+ for (let i = rects.length; i < this.tooltips.children.length; i++) {
1757
+ this.tooltips.removeChild(this.tooltips.children[i]);
1758
+ }
1759
+ }
1760
+ getUserCursor(index) {
1761
+ let child = this.tooltips.children[index];
1762
+ if (child) {
1763
+ const anchor = child.children[0];
1764
+ return {
1765
+ cursor: child,
1766
+ anchor,
1767
+ userTip: anchor.children[0]
1768
+ };
1769
+ }
1770
+ const userTip = createElement('span', {
1882
1771
  styles: {
1883
- border: 'none',
1884
- width: '100%',
1885
- display: 'block',
1886
- height: '100%',
1887
- position: 'relative',
1888
- top: this.isWindows ? '3px' : '0'
1772
+ position: 'absolute',
1773
+ left: '50%',
1774
+ transform: 'translateX(-50%)',
1775
+ marginBottom: '2px',
1776
+ bottom: '100%',
1777
+ whiteSpace: 'nowrap',
1778
+ color: '#fff',
1779
+ boxShadow: '0 1px 2px rgba(0,0,0,.1)',
1780
+ opacity: 0.8,
1781
+ borderRadius: '3px',
1782
+ padding: '3px 5px',
1783
+ pointerEvents: 'none',
1889
1784
  }
1890
1785
  });
1786
+ const anchor = createElement('span', {
1787
+ styles: {
1788
+ position: 'absolute',
1789
+ top: '-2px',
1790
+ left: '-2px',
1791
+ width: '5px',
1792
+ height: '5px',
1793
+ borderRadius: '50%',
1794
+ pointerEvents: 'auto',
1795
+ pointer: 'cursor',
1796
+ },
1797
+ children: [userTip]
1798
+ });
1799
+ child = createElement('span', {
1800
+ styles: {
1801
+ position: 'absolute',
1802
+ },
1803
+ children: [
1804
+ anchor
1805
+ ]
1806
+ });
1807
+ this.tooltips.append(child);
1808
+ return {
1809
+ cursor: child,
1810
+ anchor,
1811
+ userTip
1812
+ };
1891
1813
  }
1892
1814
  };
1893
- MagicInput = __decorate([
1815
+ CollaborateCursor = __decorate([
1894
1816
  Injectable(),
1895
- __metadata("design:paramtypes", [Parser,
1896
- Keyboard,
1897
- Commander,
1898
- Selection,
1899
- Controller,
1817
+ __param(4, Optional()),
1818
+ __metadata("design:paramtypes", [Injector,
1819
+ SelectionBridge,
1900
1820
  Scheduler,
1901
- Injector])
1902
- ], MagicInput);
1821
+ Selection,
1822
+ CollaborateSelectionAwarenessDelegate])
1823
+ ], CollaborateCursor);
1903
1824
 
1904
1825
  class NativeCaret {
1905
1826
  set nativeRange(range) {
@@ -2015,14 +1936,13 @@ let NativeInput = class NativeInput extends Input {
2015
1936
  get disabled() {
2016
1937
  return this._disabled;
2017
1938
  }
2018
- constructor(injector, parser, scheduler, selection, keyboard, renderer, commander, controller) {
1939
+ constructor(injector, parser, scheduler, selection, keyboard, domAdapter, commander, controller) {
2019
1940
  super();
2020
- this.injector = injector;
2021
1941
  this.parser = parser;
2022
1942
  this.scheduler = scheduler;
2023
1943
  this.selection = selection;
2024
1944
  this.keyboard = keyboard;
2025
- this.renderer = renderer;
1945
+ this.domAdapter = domAdapter;
2026
1946
  this.commander = commander;
2027
1947
  this.controller = controller;
2028
1948
  this.caret = new NativeCaret(this.scheduler);
@@ -2250,7 +2170,7 @@ let NativeInput = class NativeInput extends Input {
2250
2170
  if (!range) {
2251
2171
  break;
2252
2172
  }
2253
- const location = this.renderer.getLocationByNativeNode(range.startContainer);
2173
+ const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
2254
2174
  const startSlot = this.selection.startSlot;
2255
2175
  if (startSlot) {
2256
2176
  this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
@@ -2261,7 +2181,7 @@ let NativeInput = class NativeInput extends Input {
2261
2181
  case 'insertReplacementText': {
2262
2182
  this.composition = false;
2263
2183
  const range = ev.getTargetRanges()[0];
2264
- const location = this.renderer.getLocationByNativeNode(range.startContainer);
2184
+ const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
2265
2185
  const startSlot = this.selection.startSlot;
2266
2186
  this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
2267
2187
  this.commander.delete();
@@ -2315,7 +2235,7 @@ let NativeInput = class NativeInput extends Input {
2315
2235
  }
2316
2236
  if (ev.inputType === 'insertReplacementText') {
2317
2237
  const range = ev.getTargetRanges()[0];
2318
- const location = this.renderer.getLocationByNativeNode(range.startContainer);
2238
+ const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
2319
2239
  const startSlot = this.selection.startSlot;
2320
2240
  this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
2321
2241
  this.commander.delete();
@@ -2373,561 +2293,59 @@ NativeInput = __decorate([
2373
2293
  Scheduler,
2374
2294
  Selection,
2375
2295
  Keyboard,
2376
- Renderer,
2296
+ DomAdapter,
2377
2297
  Commander,
2378
2298
  Controller])
2379
2299
  ], NativeInput);
2380
2300
 
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 = [{
2301
+ class BrowserModule {
2302
+ constructor(host, config) {
2303
+ this.host = host;
2304
+ this.config = config;
2305
+ const { mask, wrapper } = BrowserModule.createLayout();
2306
+ wrapper.prepend(config.adapter.host);
2307
+ if (config.minHeight) {
2308
+ config.adapter.host.style.minHeight = config.minHeight;
2309
+ }
2310
+ this.providers = [{
2496
2311
  provide: EDITOR_OPTIONS,
2497
- useValue: options
2312
+ useValue: config
2498
2313
  }, {
2499
2314
  provide: VIEW_CONTAINER,
2500
2315
  useValue: wrapper
2501
2316
  }, {
2502
2317
  provide: VIEW_DOCUMENT,
2503
- useValue: doc
2318
+ useValue: config.adapter.host
2504
2319
  }, {
2505
2320
  provide: VIEW_MASK,
2506
2321
  useValue: mask
2507
- }, {
2508
- provide: NativeRenderer,
2509
- useExisting: DomRenderer
2510
2322
  }, {
2511
2323
  provide: NativeSelectionBridge,
2512
2324
  useExisting: SelectionBridge
2513
2325
  }, {
2514
2326
  provide: Input,
2515
- useClass: options.useContentEditable ? NativeInput : MagicInput
2327
+ useClass: config.useContentEditable ? NativeInput : MagicInput
2516
2328
  }, {
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
2329
+ provide: ViewAdapter,
2330
+ useFactory(v) {
2331
+ return v;
2332
+ },
2333
+ deps: [DomAdapter]
2334
+ }, {
2335
+ provide: DomAdapter,
2336
+ useValue: config.adapter
2867
2337
  },
2868
- action: () => {
2869
- history.forward();
2870
- }
2871
- });
2338
+ Parser,
2339
+ SelectionBridge,
2340
+ CollaborateCursor
2341
+ ];
2342
+ this.workbench = wrapper;
2343
+ this.host.append(wrapper);
2872
2344
  }
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
- });
2345
+ onDestroy() {
2346
+ this.workbench.remove();
2913
2347
  }
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
- });
2348
+ static createLayout() {
2931
2349
  const mask = createElement('div', {
2932
2350
  attrs: {
2933
2351
  'data-textbus-view': VIEW_MASK,
@@ -2953,20 +2371,13 @@ class Viewer extends Starter {
2953
2371
  position: 'relative',
2954
2372
  flexDirection: 'column'
2955
2373
  },
2956
- children: [doc, mask]
2374
+ children: [mask]
2957
2375
  });
2958
2376
  return {
2959
2377
  wrapper,
2960
- doc,
2961
2378
  mask
2962
2379
  };
2963
2380
  }
2964
- static cssMin(str) {
2965
- return str
2966
- .replace(/\s*(?=[>{}:;,[])/g, '')
2967
- .replace(/([>{}:;,])\s*/g, '$1')
2968
- .replace(/;}/g, '}').replace(/\s+/, ' ').trim();
2969
- }
2970
2381
  }
2971
2382
 
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 };
2383
+ 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 };