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