@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.
- package/bundles/browser-module.d.ts +33 -0
- package/bundles/{collaborate/collaborate-cursor.d.ts → collaborate-cursor.d.ts} +3 -4
- package/bundles/dom-adapter.d.ts +66 -0
- package/bundles/index.esm.js +682 -1271
- package/bundles/index.js +737 -1325
- package/bundles/{core/injection-tokens.d.ts → injection-tokens.d.ts} +2 -2
- package/bundles/{core/magic-input.d.ts → magic-input.d.ts} +2 -2
- package/bundles/{core/native-input.d.ts → native-input.d.ts} +7 -7
- package/bundles/{dom-support/parser.d.ts → parser.d.ts} +3 -16
- package/bundles/public-api.d.ts +8 -4
- package/bundles/{core/selection-bridge.d.ts → selection-bridge.d.ts} +8 -10
- package/bundles/types.d.ts +44 -0
- package/package.json +4 -4
- package/bundles/core/_api.d.ts +0 -6
- package/bundles/core/dom-renderer.d.ts +0 -53
- package/bundles/core/types.d.ts +0 -74
- package/bundles/dom-support/_api.d.ts +0 -2
- package/bundles/dom-support/output-translator.d.ts +0 -20
- package/bundles/viewer.d.ts +0 -90
package/bundles/index.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
3
|
require('reflect-metadata');
|
4
|
-
var
|
5
|
-
var core = require('@
|
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,335 @@ 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
|
140
|
-
|
141
|
-
return
|
142
|
-
|
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
|
149
|
+
const EDITOR_OPTIONS = new core.InjectionToken('EDITOR_OPTIONS');
|
153
150
|
/**
|
154
151
|
* 编辑器容器依赖注入 token
|
155
152
|
*/
|
156
|
-
const VIEW_CONTAINER = new
|
153
|
+
const VIEW_CONTAINER = new core.InjectionToken('VIEW_CONTAINER');
|
157
154
|
/**
|
158
155
|
* 编辑器容器依赖注入 token
|
159
156
|
*/
|
160
|
-
const VIEW_DOCUMENT = new
|
157
|
+
const VIEW_DOCUMENT = new core.InjectionToken('VIEW_DOCUMENT');
|
161
158
|
/**
|
162
159
|
* 编辑器容器遮罩层 token
|
163
160
|
*/
|
164
|
-
const VIEW_MASK = new
|
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 端浏览器渲染能力桥接器抽象类,提供了 DOM 元素查询能力,具体渲染能力由各前端框架实现相应桥接
|
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 = core$1.createBidirectionalMapping(a => {
|
328
|
+
return a instanceof core$1.ComponentInstance;
|
329
|
+
});
|
330
|
+
this.slotRootNativeElementCaches = core$1.createBidirectionalMapping(a => {
|
331
|
+
return a instanceof core$1.Slot;
|
332
|
+
});
|
333
|
+
this.slotRootVElementCaches = new WeakMap();
|
334
|
+
}
|
335
|
+
render(rootComponent) {
|
336
|
+
const view = this.componentRender(rootComponent);
|
337
|
+
return this.mount(this.host, view);
|
338
|
+
}
|
339
|
+
copy() {
|
340
|
+
document.execCommand('copy');
|
341
|
+
}
|
342
|
+
/**
|
343
|
+
* 根据组件获取组件的根 DOM 节点
|
344
|
+
* @param component
|
345
|
+
*/
|
346
|
+
getNativeNodeByComponent(component) {
|
347
|
+
return this.componentRootElementCaches.get(component) || null;
|
348
|
+
}
|
349
|
+
/**
|
350
|
+
* 根据 DOM 节点,获对对应的组件根节点,如传入的 DOM 节点不为组件的根节点,则返回 null
|
351
|
+
* @param node
|
352
|
+
*/
|
353
|
+
getComponentByNativeNode(node) {
|
354
|
+
return this.componentRootElementCaches.get(node) || null;
|
355
|
+
}
|
356
|
+
/**
|
357
|
+
* 根据插槽获取插槽的根 DOM 节点
|
358
|
+
* @param slot
|
359
|
+
*/
|
360
|
+
getNativeNodeBySlot(slot) {
|
361
|
+
return this.slotRootNativeElementCaches.get(slot) || null;
|
362
|
+
}
|
363
|
+
/**
|
364
|
+
* 根据 DOM 节点,获对对应的插槽根节点,如传入的 DOM 节点不为插槽的根节点,则返回 null
|
365
|
+
* @param node
|
366
|
+
*/
|
367
|
+
getSlotByNativeNode(node) {
|
368
|
+
return this.slotRootNativeElementCaches.get(node) || null;
|
369
|
+
}
|
370
|
+
/**
|
371
|
+
* 获取插槽内容节点集合
|
372
|
+
* @param slot
|
373
|
+
*/
|
374
|
+
getNodesBySlot(slot) {
|
375
|
+
const rootNativeNode = this.getNativeNodeBySlot(slot);
|
376
|
+
if (!rootNativeNode) {
|
377
|
+
return [];
|
378
|
+
}
|
379
|
+
const rootVNode = this.slotRootVElementCaches.get(slot);
|
380
|
+
const getNodes = (vElement, nativeNode, result) => {
|
381
|
+
if (vElement.location) {
|
382
|
+
result.push(nativeNode);
|
383
|
+
}
|
384
|
+
for (let i = 0; i < vElement.children.length; i++) {
|
385
|
+
const vChild = vElement.children[i];
|
386
|
+
const nativeChild = nativeNode.childNodes[i];
|
387
|
+
if (vChild instanceof core$1.VElement) {
|
388
|
+
getNodes(vChild, nativeChild, result);
|
389
|
+
}
|
390
|
+
else if (vChild instanceof core$1.VTextNode) {
|
391
|
+
result.push(nativeChild);
|
392
|
+
}
|
393
|
+
else {
|
394
|
+
result.push(this.getNativeNodeByComponent(vChild));
|
395
|
+
}
|
396
|
+
}
|
397
|
+
return result;
|
398
|
+
};
|
399
|
+
return getNodes(rootVNode, rootNativeNode, []);
|
400
|
+
}
|
401
|
+
/**
|
402
|
+
* 获取原生节点的原始数据在文档中的位置
|
403
|
+
* @param node
|
404
|
+
*/
|
405
|
+
getLocationByNativeNode(node) {
|
406
|
+
let slotRootNode = node;
|
407
|
+
while (!this.slotRootNativeElementCaches.get(slotRootNode)) {
|
408
|
+
slotRootNode = slotRootNode.parentNode;
|
409
|
+
if (!slotRootNode) {
|
410
|
+
return null;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
const slot = this.slotRootNativeElementCaches.get(slotRootNode);
|
414
|
+
const rootVNode = this.slotRootVElementCaches.get(slot);
|
415
|
+
const getLocation = (target, tree, vNodeTree) => {
|
416
|
+
if (target === tree) {
|
417
|
+
return Object.assign({}, vNodeTree.location);
|
418
|
+
}
|
419
|
+
const childNodes = tree.childNodes;
|
420
|
+
for (let i = 0; i < childNodes.length; i++) {
|
421
|
+
const child = vNodeTree.children[i];
|
422
|
+
const nativeChild = tree.childNodes[i];
|
423
|
+
if (nativeChild === target) {
|
424
|
+
if (child instanceof core$1.ComponentInstance) {
|
425
|
+
const index = child.parent.indexOf(child);
|
426
|
+
return {
|
427
|
+
slot: child.parent,
|
428
|
+
startIndex: index,
|
429
|
+
endIndex: index + 1
|
430
|
+
};
|
431
|
+
}
|
432
|
+
return child.location;
|
433
|
+
}
|
434
|
+
else if (child instanceof core$1.VElement) {
|
435
|
+
let r = null;
|
436
|
+
if (nativeChild.nodeType === Node.ELEMENT_NODE) {
|
437
|
+
r = getLocation(target, nativeChild, child);
|
438
|
+
}
|
439
|
+
if (r) {
|
440
|
+
return r;
|
441
|
+
}
|
442
|
+
}
|
443
|
+
}
|
444
|
+
return null;
|
445
|
+
};
|
446
|
+
return getLocation(node, slotRootNode, rootVNode);
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
169
450
|
/**
|
170
451
|
* Textbus PC 端选区桥接实现
|
171
452
|
*/
|
172
453
|
exports.SelectionBridge = class SelectionBridge {
|
173
|
-
constructor(config, injector, controller, selection, rootComponentRef, input,
|
454
|
+
constructor(config, injector, controller, selection, rootComponentRef, input, domAdapter) {
|
174
455
|
this.config = config;
|
175
|
-
this.injector = injector;
|
176
|
-
this.controller = controller;
|
177
456
|
this.selection = selection;
|
178
457
|
this.rootComponentRef = rootComponentRef;
|
179
458
|
this.input = input;
|
180
|
-
this.
|
459
|
+
this.domAdapter = domAdapter;
|
181
460
|
this.nativeSelection = document.getSelection();
|
182
|
-
this.selectionMaskElement = createElement('style');
|
183
461
|
this.selectionChangeEvent = new stream.Subject();
|
184
462
|
this.subs = [];
|
185
463
|
this.connector = null;
|
186
464
|
this.ignoreSelectionChange = false;
|
187
465
|
this.changeFromUser = false;
|
188
466
|
this.docContainer = injector.get(VIEW_DOCUMENT);
|
189
|
-
this.maskContainer = injector.get(VIEW_MASK);
|
190
467
|
this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(stream.filter(() => {
|
191
468
|
return !controller.readonly;
|
192
469
|
}));
|
193
|
-
document.head.appendChild(this.selectionMaskElement);
|
194
470
|
this.sub = this.onSelectionChange.subscribe((r) => {
|
195
471
|
if (r) {
|
196
472
|
input.focus(r, this.changeFromUser);
|
@@ -486,22 +762,14 @@ exports.SelectionBridge = class SelectionBridge {
|
|
486
762
|
const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
|
487
763
|
if (!this.docContainer.contains(selection.focusNode)) {
|
488
764
|
if (isFocusEnd) {
|
489
|
-
const
|
490
|
-
if (!vEle) {
|
491
|
-
return;
|
492
|
-
}
|
493
|
-
const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
|
765
|
+
const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.first);
|
494
766
|
if (!nativeNode) {
|
495
767
|
return;
|
496
768
|
}
|
497
769
|
nativeRange.setEndAfter(nativeNode.lastChild);
|
498
770
|
}
|
499
771
|
else {
|
500
|
-
const
|
501
|
-
if (!vEle) {
|
502
|
-
return;
|
503
|
-
}
|
504
|
-
const nativeNode = this.renderer.getNativeNodeByVNode(vEle);
|
772
|
+
const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.last);
|
505
773
|
if (!nativeNode) {
|
506
774
|
return;
|
507
775
|
}
|
@@ -558,30 +826,28 @@ exports.SelectionBridge = class SelectionBridge {
|
|
558
826
|
}
|
559
827
|
findSelectedNodeAndOffset(slot, offset) {
|
560
828
|
const prev = slot.getContentAtIndex(offset - 1);
|
561
|
-
const
|
829
|
+
const nodes = this.domAdapter.getNodesBySlot(slot);
|
562
830
|
if (prev) {
|
563
831
|
if (typeof prev !== 'string') {
|
564
|
-
const
|
565
|
-
const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
|
832
|
+
const nativeNode = this.domAdapter.getNativeNodeByComponent(prev);
|
566
833
|
return {
|
567
834
|
node: nativeNode.parentNode,
|
568
835
|
offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode) + 1
|
569
836
|
};
|
570
837
|
}
|
571
838
|
else if (prev === '\n') {
|
572
|
-
for (const
|
573
|
-
if (
|
839
|
+
for (const node of nodes) {
|
840
|
+
if (node instanceof Text) {
|
574
841
|
continue;
|
575
842
|
}
|
576
|
-
if (
|
577
|
-
const position = this.
|
843
|
+
if (node.nodeName === 'BR') {
|
844
|
+
const position = this.domAdapter.getLocationByNativeNode(node);
|
578
845
|
if (position) {
|
579
846
|
if (position.endIndex === offset) {
|
580
|
-
const
|
581
|
-
const parentNode = nativeNode.parentNode;
|
847
|
+
const parentNode = node.parentNode;
|
582
848
|
return {
|
583
849
|
node: parentNode,
|
584
|
-
offset: Array.from(parentNode.childNodes).indexOf(
|
850
|
+
offset: Array.from(parentNode.childNodes).indexOf(node) + 1
|
585
851
|
};
|
586
852
|
}
|
587
853
|
}
|
@@ -591,36 +857,33 @@ exports.SelectionBridge = class SelectionBridge {
|
|
591
857
|
}
|
592
858
|
const current = slot.getContentAtIndex(offset);
|
593
859
|
if (current && typeof current !== 'string') {
|
594
|
-
const
|
595
|
-
const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
|
860
|
+
const nativeNode = this.domAdapter.getNativeNodeByComponent(current);
|
596
861
|
return {
|
597
862
|
node: nativeNode.parentNode,
|
598
863
|
offset: Array.from(nativeNode.parentNode.childNodes).indexOf(nativeNode)
|
599
864
|
};
|
600
865
|
}
|
601
|
-
for (const
|
602
|
-
if (
|
603
|
-
if (
|
604
|
-
const position = this.
|
866
|
+
for (const node of nodes) {
|
867
|
+
if (node instanceof Element) {
|
868
|
+
if (node.tagName === 'BR') {
|
869
|
+
const position = this.domAdapter.getLocationByNativeNode(node);
|
605
870
|
if (position) {
|
606
871
|
if (position.startIndex === offset) {
|
607
|
-
const
|
608
|
-
const parentNode = nativeNode.parentNode;
|
872
|
+
const parentNode = node.parentNode;
|
609
873
|
return {
|
610
874
|
node: parentNode,
|
611
|
-
offset: Array.from(parentNode.childNodes).indexOf(
|
875
|
+
offset: Array.from(parentNode.childNodes).indexOf(node)
|
612
876
|
};
|
613
877
|
}
|
614
878
|
}
|
615
879
|
}
|
616
880
|
continue;
|
617
881
|
}
|
618
|
-
const position = this.
|
882
|
+
const position = this.domAdapter.getLocationByNativeNode(node);
|
619
883
|
if (position) {
|
620
884
|
if (offset >= position.startIndex && offset <= position.endIndex) {
|
621
|
-
const nativeNode = this.renderer.getNativeNodeByVNode(vNode);
|
622
885
|
return {
|
623
|
-
node:
|
886
|
+
node: node,
|
624
887
|
offset: offset - position.startIndex
|
625
888
|
};
|
626
889
|
}
|
@@ -631,10 +894,10 @@ exports.SelectionBridge = class SelectionBridge {
|
|
631
894
|
getCorrectedPosition(node, offset, toAfter, excludeNodes = []) {
|
632
895
|
excludeNodes.push(node);
|
633
896
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
634
|
-
const containerPosition = this.
|
897
|
+
const containerPosition = this.domAdapter.getLocationByNativeNode(node);
|
635
898
|
const childNode = node.childNodes[offset];
|
636
899
|
if (childNode) {
|
637
|
-
const childPosition = this.
|
900
|
+
const childPosition = this.domAdapter.getLocationByNativeNode(childNode);
|
638
901
|
if (childPosition) {
|
639
902
|
if (containerPosition) {
|
640
903
|
return {
|
@@ -648,7 +911,7 @@ exports.SelectionBridge = class SelectionBridge {
|
|
648
911
|
}
|
649
912
|
const prevNode = node.childNodes[offset - 1];
|
650
913
|
if (prevNode) {
|
651
|
-
const prevPosition = this.
|
914
|
+
const prevPosition = this.domAdapter.getLocationByNativeNode(prevNode);
|
652
915
|
if (prevPosition && containerPosition) {
|
653
916
|
return {
|
654
917
|
slot: prevPosition.slot,
|
@@ -669,7 +932,7 @@ exports.SelectionBridge = class SelectionBridge {
|
|
669
932
|
return this.findFocusNodeByParent(node, toAfter, excludeNodes);
|
670
933
|
}
|
671
934
|
else if (node.nodeType === Node.TEXT_NODE) {
|
672
|
-
const containerPosition = this.
|
935
|
+
const containerPosition = this.domAdapter.getLocationByNativeNode(node);
|
673
936
|
if (containerPosition) {
|
674
937
|
return {
|
675
938
|
slot: containerPosition.slot,
|
@@ -693,7 +956,7 @@ exports.SelectionBridge = class SelectionBridge {
|
|
693
956
|
return this.findFocusNodeByParent(node, toAfter, excludeNodes);
|
694
957
|
}
|
695
958
|
excludeNodes.push(node);
|
696
|
-
const position = this.
|
959
|
+
const position = this.domAdapter.getLocationByNativeNode(node);
|
697
960
|
if (position) {
|
698
961
|
return {
|
699
962
|
slot: position.slot,
|
@@ -713,7 +976,7 @@ exports.SelectionBridge = class SelectionBridge {
|
|
713
976
|
findFocusNodeByParent(node, toAfter, excludeNodes) {
|
714
977
|
const parentNode = node.parentNode;
|
715
978
|
if (parentNode) {
|
716
|
-
const parentPosition = this.
|
979
|
+
const parentPosition = this.domAdapter.getLocationByNativeNode(parentNode);
|
717
980
|
if (parentPosition) {
|
718
981
|
return {
|
719
982
|
slot: parentPosition.slot,
|
@@ -727,665 +990,51 @@ exports.SelectionBridge = class SelectionBridge {
|
|
727
990
|
}
|
728
991
|
};
|
729
992
|
exports.SelectionBridge = __decorate([
|
730
|
-
|
731
|
-
__param(0,
|
732
|
-
__metadata("design:paramtypes", [Object,
|
733
|
-
core.Controller,
|
734
|
-
core.Selection,
|
735
|
-
core.RootComponentRef,
|
993
|
+
core.Injectable(),
|
994
|
+
__param(0, core.Inject(EDITOR_OPTIONS)),
|
995
|
+
__metadata("design:paramtypes", [Object, core.Injector,
|
996
|
+
core$1.Controller,
|
997
|
+
core$1.Selection,
|
998
|
+
core$1.RootComponentRef,
|
736
999
|
Input,
|
737
|
-
|
1000
|
+
DomAdapter])
|
738
1001
|
], exports.SelectionBridge);
|
739
1002
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
1003
|
+
const iframeHTML = `
|
1004
|
+
<!DOCTYPE html>
|
1005
|
+
<html>
|
1006
|
+
<head>
|
1007
|
+
<meta charset="UTF-8">
|
1008
|
+
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
1009
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
1010
|
+
<title>Textbus</title>
|
1011
|
+
<style>
|
1012
|
+
html {position: fixed; left:0; overflow: hidden}
|
1013
|
+
html, body{height: 100%;width:100%}
|
1014
|
+
body{margin:0; overflow: hidden}
|
1015
|
+
textarea{width: 2000px;height: 100%;opacity: 0; padding: 0; outline: none; border: none; position: absolute; left:0; top:0;}
|
1016
|
+
</style>
|
1017
|
+
</head>
|
1018
|
+
<body>
|
1019
|
+
</body>
|
1020
|
+
</html>
|
1021
|
+
`;
|
1022
|
+
class ExperimentalCaret {
|
1023
|
+
get rect() {
|
1024
|
+
return this.caret.getBoundingClientRect();
|
1025
|
+
}
|
1026
|
+
set display(v) {
|
1027
|
+
this._display = v;
|
1028
|
+
this.caret.style.visibility = v ? 'visible' : 'hidden';
|
1029
|
+
}
|
1030
|
+
get display() {
|
1031
|
+
return this._display;
|
1032
|
+
}
|
1033
|
+
constructor(scheduler, editorMask) {
|
752
1034
|
this.scheduler = scheduler;
|
753
|
-
this.
|
754
|
-
this.
|
755
|
-
this.
|
756
|
-
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', {
|
1035
|
+
this.editorMask = editorMask;
|
1036
|
+
this.compositionState = null;
|
1037
|
+
this.compositionElement = createElement('span', {
|
1389
1038
|
styles: {
|
1390
1039
|
textDecoration: 'underline'
|
1391
1040
|
}
|
@@ -1751,10 +1400,10 @@ exports.MagicInput = class MagicInput extends Input {
|
|
1751
1400
|
}));
|
1752
1401
|
}
|
1753
1402
|
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
|
1403
|
+
const slot = this.parser.parse(html, new core$1.Slot([
|
1404
|
+
core$1.ContentType.BlockComponent,
|
1405
|
+
core$1.ContentType.InlineComponent,
|
1406
|
+
core$1.ContentType.Text
|
1758
1407
|
]));
|
1759
1408
|
this.commander.paste(slot, text);
|
1760
1409
|
}
|
@@ -1811,10 +1460,10 @@ exports.MagicInput = class MagicInput extends Input {
|
|
1811
1460
|
this.caret.compositionState = this.compositionState = null;
|
1812
1461
|
startIndex = this.selection.startOffset;
|
1813
1462
|
const startSlot = this.selection.startSlot;
|
1814
|
-
const event = new core.Event(startSlot, {
|
1463
|
+
const event = new core$1.Event(startSlot, {
|
1815
1464
|
index: startIndex
|
1816
1465
|
});
|
1817
|
-
core.invokeListener(startSlot.parent, 'onCompositionStart', event);
|
1466
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionStart', event);
|
1818
1467
|
}), stream.fromEvent(textarea, 'compositionupdate').pipe(stream.filter(() => {
|
1819
1468
|
return !this.ignoreComposition;
|
1820
1469
|
})).pipe(stream.distinctUntilChanged((prev, next) => {
|
@@ -1831,11 +1480,11 @@ exports.MagicInput = class MagicInput extends Input {
|
|
1831
1480
|
data: ev.data
|
1832
1481
|
};
|
1833
1482
|
this.caret.refresh(true);
|
1834
|
-
const event = new core.Event(startSlot, {
|
1483
|
+
const event = new core$1.Event(startSlot, {
|
1835
1484
|
index: startIndex,
|
1836
1485
|
data: ev.data
|
1837
1486
|
});
|
1838
|
-
core.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
|
1487
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
|
1839
1488
|
}));
|
1840
1489
|
let isCompositionEnd = false;
|
1841
1490
|
this.subscription.add(stream.merge(stream.fromEvent(textarea, 'beforeinput').pipe(stream.filter(ev => {
|
@@ -1859,49 +1508,321 @@ exports.MagicInput = class MagicInput extends Input {
|
|
1859
1508
|
textarea.value = '';
|
1860
1509
|
return ev.data;
|
1861
1510
|
}))).subscribe(text => {
|
1862
|
-
var _a;
|
1863
1511
|
this.composition = false;
|
1864
1512
|
this.caret.compositionState = this.compositionState = null;
|
1865
|
-
|
1513
|
+
const compositionElement = this.caret.compositionElement;
|
1514
|
+
let nextSibling = compositionElement.nextSibling;
|
1515
|
+
while (nextSibling) {
|
1516
|
+
if (!nextSibling.textContent) {
|
1517
|
+
const next = nextSibling.nextSibling;
|
1518
|
+
nextSibling.remove();
|
1519
|
+
nextSibling = next;
|
1520
|
+
continue;
|
1521
|
+
}
|
1522
|
+
nextSibling.remove();
|
1523
|
+
break;
|
1524
|
+
}
|
1525
|
+
compositionElement.remove();
|
1866
1526
|
if (text) {
|
1867
1527
|
this.commander.write(text);
|
1868
1528
|
}
|
1869
1529
|
if (isCompositionEnd) {
|
1870
1530
|
const startSlot = this.selection.startSlot;
|
1871
1531
|
if (startSlot) {
|
1872
|
-
const event = new core.Event(startSlot, null);
|
1873
|
-
core.invokeListener(startSlot.parent, 'onCompositionEnd', event);
|
1532
|
+
const event = new core$1.Event(startSlot, null);
|
1533
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionEnd', event);
|
1874
1534
|
}
|
1875
1535
|
}
|
1876
|
-
isCompositionEnd = false;
|
1877
|
-
}));
|
1536
|
+
isCompositionEnd = false;
|
1537
|
+
}));
|
1538
|
+
}
|
1539
|
+
createEditableFrame() {
|
1540
|
+
return createElement('iframe', {
|
1541
|
+
attrs: {
|
1542
|
+
scrolling: 'no'
|
1543
|
+
},
|
1544
|
+
styles: {
|
1545
|
+
border: 'none',
|
1546
|
+
width: '100%',
|
1547
|
+
display: 'block',
|
1548
|
+
height: '100%',
|
1549
|
+
position: 'relative',
|
1550
|
+
top: this.isWindows ? '3px' : '0'
|
1551
|
+
}
|
1552
|
+
});
|
1553
|
+
}
|
1554
|
+
};
|
1555
|
+
exports.MagicInput = __decorate([
|
1556
|
+
core.Injectable(),
|
1557
|
+
__metadata("design:paramtypes", [exports.Parser,
|
1558
|
+
core$1.Keyboard,
|
1559
|
+
core$1.Commander,
|
1560
|
+
core$1.Selection,
|
1561
|
+
core$1.Controller,
|
1562
|
+
core$1.Scheduler,
|
1563
|
+
core.Injector])
|
1564
|
+
], exports.MagicInput);
|
1565
|
+
|
1566
|
+
/**
|
1567
|
+
* 远程光标绘制范围计算代理类,可用于定制特定场景下的远程选区绘制,如表格有选区,不会遵守常见的文档流形式
|
1568
|
+
*/
|
1569
|
+
class CollaborateSelectionAwarenessDelegate {
|
1570
|
+
}
|
1571
|
+
/**
|
1572
|
+
* 协作光标绘制类
|
1573
|
+
*/
|
1574
|
+
exports.CollaborateCursor = class CollaborateCursor {
|
1575
|
+
constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
|
1576
|
+
this.nativeSelection = nativeSelection;
|
1577
|
+
this.scheduler = scheduler;
|
1578
|
+
this.selection = selection;
|
1579
|
+
this.awarenessDelegate = awarenessDelegate;
|
1580
|
+
this.host = createElement('div', {
|
1581
|
+
styles: {
|
1582
|
+
position: 'absolute',
|
1583
|
+
left: 0,
|
1584
|
+
top: 0,
|
1585
|
+
width: '100%',
|
1586
|
+
height: '100%',
|
1587
|
+
pointerEvents: 'none',
|
1588
|
+
zIndex: 1
|
1589
|
+
}
|
1590
|
+
});
|
1591
|
+
this.canvasContainer = createElement('div', {
|
1592
|
+
styles: {
|
1593
|
+
position: 'absolute',
|
1594
|
+
left: 0,
|
1595
|
+
top: 0,
|
1596
|
+
width: '100%',
|
1597
|
+
height: '100%',
|
1598
|
+
overflow: 'hidden'
|
1599
|
+
}
|
1600
|
+
});
|
1601
|
+
this.canvas = createElement('canvas', {
|
1602
|
+
styles: {
|
1603
|
+
position: 'absolute',
|
1604
|
+
opacity: 0.5,
|
1605
|
+
left: 0,
|
1606
|
+
top: 0,
|
1607
|
+
width: '100%',
|
1608
|
+
height: document.documentElement.clientHeight + 'px',
|
1609
|
+
pointerEvents: 'none',
|
1610
|
+
}
|
1611
|
+
});
|
1612
|
+
this.context = this.canvas.getContext('2d');
|
1613
|
+
this.tooltips = createElement('div', {
|
1614
|
+
styles: {
|
1615
|
+
position: 'absolute',
|
1616
|
+
left: 0,
|
1617
|
+
top: 0,
|
1618
|
+
width: '100%',
|
1619
|
+
height: '100%',
|
1620
|
+
pointerEvents: 'none',
|
1621
|
+
fontSize: '12px',
|
1622
|
+
zIndex: 10
|
1623
|
+
}
|
1624
|
+
});
|
1625
|
+
this.onRectsChange = new stream.Subject();
|
1626
|
+
this.subscription = new stream.Subscription();
|
1627
|
+
this.currentSelection = [];
|
1628
|
+
this.container = injector.get(VIEW_CONTAINER);
|
1629
|
+
this.canvasContainer.append(this.canvas);
|
1630
|
+
this.host.append(this.canvasContainer, this.tooltips);
|
1631
|
+
this.container.prepend(this.host);
|
1632
|
+
this.subscription.add(this.onRectsChange.subscribe(rects => {
|
1633
|
+
for (const rect of rects) {
|
1634
|
+
this.context.fillStyle = rect.color;
|
1635
|
+
this.context.beginPath();
|
1636
|
+
this.context.rect(rect.left, rect.top, rect.width, rect.height);
|
1637
|
+
this.context.fill();
|
1638
|
+
this.context.closePath();
|
1639
|
+
}
|
1640
|
+
}), stream.fromEvent(window, 'resize').subscribe(() => {
|
1641
|
+
this.canvas.style.height = document.documentElement.clientHeight + 'px';
|
1642
|
+
this.refresh();
|
1643
|
+
}), this.scheduler.onDocChanged.subscribe(() => {
|
1644
|
+
this.refresh();
|
1645
|
+
}));
|
1646
|
+
}
|
1647
|
+
/**
|
1648
|
+
* 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
|
1649
|
+
*/
|
1650
|
+
refresh() {
|
1651
|
+
this.draw(this.currentSelection);
|
1652
|
+
}
|
1653
|
+
destroy() {
|
1654
|
+
this.subscription.unsubscribe();
|
1655
|
+
}
|
1656
|
+
/**
|
1657
|
+
* 根据远程用户光标位置,绘制协作光标
|
1658
|
+
* @param paths
|
1659
|
+
*/
|
1660
|
+
draw(paths) {
|
1661
|
+
this.currentSelection = paths;
|
1662
|
+
const containerRect = this.container.getBoundingClientRect();
|
1663
|
+
this.canvas.style.top = containerRect.top * -1 + 'px';
|
1664
|
+
this.canvas.width = this.canvas.offsetWidth;
|
1665
|
+
this.canvas.height = this.canvas.offsetHeight;
|
1666
|
+
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
1667
|
+
const users = [];
|
1668
|
+
paths.filter(i => {
|
1669
|
+
return i.paths.anchor.length && i.paths.focus.length;
|
1670
|
+
}).forEach(item => {
|
1671
|
+
const anchorPaths = [...item.paths.anchor];
|
1672
|
+
const focusPaths = [...item.paths.focus];
|
1673
|
+
const anchorOffset = anchorPaths.pop();
|
1674
|
+
const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
|
1675
|
+
const focusOffset = focusPaths.pop();
|
1676
|
+
const focusSlot = this.selection.findSlotByPaths(focusPaths);
|
1677
|
+
if (!anchorSlot || !focusSlot) {
|
1678
|
+
return;
|
1679
|
+
}
|
1680
|
+
const { focus, anchor } = this.nativeSelection.getPositionByRange({
|
1681
|
+
focusOffset,
|
1682
|
+
anchorOffset,
|
1683
|
+
focusSlot,
|
1684
|
+
anchorSlot
|
1685
|
+
});
|
1686
|
+
if (!focus || !anchor) {
|
1687
|
+
return;
|
1688
|
+
}
|
1689
|
+
const nativeRange = document.createRange();
|
1690
|
+
nativeRange.setStart(anchor.node, anchor.offset);
|
1691
|
+
nativeRange.setEnd(focus.node, focus.offset);
|
1692
|
+
if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
|
1693
|
+
nativeRange.setStart(focus.node, focus.offset);
|
1694
|
+
nativeRange.setEnd(anchor.node, anchor.offset);
|
1695
|
+
}
|
1696
|
+
let rects = false;
|
1697
|
+
if (this.awarenessDelegate) {
|
1698
|
+
rects = this.awarenessDelegate.getRects({
|
1699
|
+
focusOffset,
|
1700
|
+
anchorOffset,
|
1701
|
+
focusSlot,
|
1702
|
+
anchorSlot
|
1703
|
+
}, nativeRange);
|
1704
|
+
}
|
1705
|
+
if (!rects) {
|
1706
|
+
rects = nativeRange.getClientRects();
|
1707
|
+
}
|
1708
|
+
const selectionRects = [];
|
1709
|
+
for (let i = rects.length - 1; i >= 0; i--) {
|
1710
|
+
const rect = rects[i];
|
1711
|
+
selectionRects.push({
|
1712
|
+
id: item.id,
|
1713
|
+
color: item.color,
|
1714
|
+
username: item.username,
|
1715
|
+
left: rect.left - containerRect.left,
|
1716
|
+
top: rect.top,
|
1717
|
+
width: rect.width,
|
1718
|
+
height: rect.height,
|
1719
|
+
});
|
1720
|
+
}
|
1721
|
+
this.onRectsChange.next(selectionRects);
|
1722
|
+
const cursorRange = nativeRange.cloneRange();
|
1723
|
+
cursorRange.setStart(focus.node, focus.offset);
|
1724
|
+
cursorRange.collapse(true);
|
1725
|
+
const cursorRect = getLayoutRectByRange(cursorRange);
|
1726
|
+
const rect = {
|
1727
|
+
id: item.id,
|
1728
|
+
username: item.username,
|
1729
|
+
color: item.color,
|
1730
|
+
left: cursorRect.left - containerRect.left,
|
1731
|
+
top: cursorRect.top - containerRect.top,
|
1732
|
+
width: 1,
|
1733
|
+
height: cursorRect.height
|
1734
|
+
};
|
1735
|
+
if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
|
1736
|
+
return;
|
1737
|
+
}
|
1738
|
+
users.push(rect);
|
1739
|
+
});
|
1740
|
+
this.drawUserCursor(users);
|
1878
1741
|
}
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1742
|
+
drawUserCursor(rects) {
|
1743
|
+
for (let i = 0; i < rects.length; i++) {
|
1744
|
+
const rect = rects[i];
|
1745
|
+
const { cursor, userTip, anchor } = this.getUserCursor(i);
|
1746
|
+
Object.assign(cursor.style, {
|
1747
|
+
left: rect.left + 'px',
|
1748
|
+
top: rect.top + 'px',
|
1749
|
+
width: rect.width + 'px',
|
1750
|
+
height: rect.height + 'px',
|
1751
|
+
background: rect.color,
|
1752
|
+
display: 'block'
|
1753
|
+
});
|
1754
|
+
anchor.style.background = rect.color;
|
1755
|
+
userTip.innerText = rect.username;
|
1756
|
+
userTip.style.background = rect.color;
|
1757
|
+
}
|
1758
|
+
for (let i = rects.length; i < this.tooltips.children.length; i++) {
|
1759
|
+
this.tooltips.removeChild(this.tooltips.children[i]);
|
1760
|
+
}
|
1761
|
+
}
|
1762
|
+
getUserCursor(index) {
|
1763
|
+
let child = this.tooltips.children[index];
|
1764
|
+
if (child) {
|
1765
|
+
const anchor = child.children[0];
|
1766
|
+
return {
|
1767
|
+
cursor: child,
|
1768
|
+
anchor,
|
1769
|
+
userTip: anchor.children[0]
|
1770
|
+
};
|
1771
|
+
}
|
1772
|
+
const userTip = createElement('span', {
|
1884
1773
|
styles: {
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1774
|
+
position: 'absolute',
|
1775
|
+
left: '50%',
|
1776
|
+
transform: 'translateX(-50%)',
|
1777
|
+
marginBottom: '2px',
|
1778
|
+
bottom: '100%',
|
1779
|
+
whiteSpace: 'nowrap',
|
1780
|
+
color: '#fff',
|
1781
|
+
boxShadow: '0 1px 2px rgba(0,0,0,.1)',
|
1782
|
+
opacity: 0.8,
|
1783
|
+
borderRadius: '3px',
|
1784
|
+
padding: '3px 5px',
|
1785
|
+
pointerEvents: 'none',
|
1891
1786
|
}
|
1892
1787
|
});
|
1788
|
+
const anchor = createElement('span', {
|
1789
|
+
styles: {
|
1790
|
+
position: 'absolute',
|
1791
|
+
top: '-2px',
|
1792
|
+
left: '-2px',
|
1793
|
+
width: '5px',
|
1794
|
+
height: '5px',
|
1795
|
+
borderRadius: '50%',
|
1796
|
+
pointerEvents: 'auto',
|
1797
|
+
pointer: 'cursor',
|
1798
|
+
},
|
1799
|
+
children: [userTip]
|
1800
|
+
});
|
1801
|
+
child = createElement('span', {
|
1802
|
+
styles: {
|
1803
|
+
position: 'absolute',
|
1804
|
+
},
|
1805
|
+
children: [
|
1806
|
+
anchor
|
1807
|
+
]
|
1808
|
+
});
|
1809
|
+
this.tooltips.append(child);
|
1810
|
+
return {
|
1811
|
+
cursor: child,
|
1812
|
+
anchor,
|
1813
|
+
userTip
|
1814
|
+
};
|
1893
1815
|
}
|
1894
1816
|
};
|
1895
|
-
exports.
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
core.
|
1901
|
-
core.
|
1902
|
-
|
1903
|
-
|
1904
|
-
], exports.MagicInput);
|
1817
|
+
exports.CollaborateCursor = __decorate([
|
1818
|
+
core.Injectable(),
|
1819
|
+
__param(4, core.Optional()),
|
1820
|
+
__metadata("design:paramtypes", [core.Injector,
|
1821
|
+
exports.SelectionBridge,
|
1822
|
+
core$1.Scheduler,
|
1823
|
+
core$1.Selection,
|
1824
|
+
CollaborateSelectionAwarenessDelegate])
|
1825
|
+
], exports.CollaborateCursor);
|
1905
1826
|
|
1906
1827
|
class NativeCaret {
|
1907
1828
|
set nativeRange(range) {
|
@@ -2005,7 +1926,7 @@ class NativeCaret {
|
|
2005
1926
|
this.subs = [];
|
2006
1927
|
}
|
2007
1928
|
}
|
2008
|
-
|
1929
|
+
let NativeInput = class NativeInput extends Input {
|
2009
1930
|
set disabled(b) {
|
2010
1931
|
this._disabled = b;
|
2011
1932
|
if (this.controller.readonly) {
|
@@ -2017,14 +1938,13 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2017
1938
|
get disabled() {
|
2018
1939
|
return this._disabled;
|
2019
1940
|
}
|
2020
|
-
constructor(injector, parser, scheduler, selection, keyboard,
|
1941
|
+
constructor(injector, parser, scheduler, selection, keyboard, domAdapter, commander, controller) {
|
2021
1942
|
super();
|
2022
|
-
this.injector = injector;
|
2023
1943
|
this.parser = parser;
|
2024
1944
|
this.scheduler = scheduler;
|
2025
1945
|
this.selection = selection;
|
2026
1946
|
this.keyboard = keyboard;
|
2027
|
-
this.
|
1947
|
+
this.domAdapter = domAdapter;
|
2028
1948
|
this.commander = commander;
|
2029
1949
|
this.controller = controller;
|
2030
1950
|
this.caret = new NativeCaret(this.scheduler);
|
@@ -2127,10 +2047,10 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2127
2047
|
}));
|
2128
2048
|
}
|
2129
2049
|
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
|
2050
|
+
const slot = this.parser.parse(html, new core$1.Slot([
|
2051
|
+
core$1.ContentType.BlockComponent,
|
2052
|
+
core$1.ContentType.InlineComponent,
|
2053
|
+
core$1.ContentType.Text
|
2134
2054
|
]));
|
2135
2055
|
this.commander.paste(slot, text);
|
2136
2056
|
}
|
@@ -2190,10 +2110,10 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2190
2110
|
this.compositionState = null;
|
2191
2111
|
startIndex = this.selection.startOffset;
|
2192
2112
|
const startSlot = this.selection.startSlot;
|
2193
|
-
const event = new core.Event(startSlot, {
|
2113
|
+
const event = new core$1.Event(startSlot, {
|
2194
2114
|
index: startIndex
|
2195
2115
|
});
|
2196
|
-
core.invokeListener(startSlot.parent, 'onCompositionStart', event);
|
2116
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionStart', event);
|
2197
2117
|
};
|
2198
2118
|
const compositionUpdate = (data) => {
|
2199
2119
|
const startSlot = this.selection.startSlot;
|
@@ -2202,11 +2122,11 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2202
2122
|
index: startIndex,
|
2203
2123
|
data
|
2204
2124
|
};
|
2205
|
-
const event = new core.Event(startSlot, {
|
2125
|
+
const event = new core$1.Event(startSlot, {
|
2206
2126
|
index: startIndex,
|
2207
2127
|
data
|
2208
2128
|
});
|
2209
|
-
core.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
|
2129
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
|
2210
2130
|
};
|
2211
2131
|
const compositionEnd = (data) => {
|
2212
2132
|
this.composition = false;
|
@@ -2215,8 +2135,8 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2215
2135
|
}
|
2216
2136
|
const startSlot = this.selection.startSlot;
|
2217
2137
|
if (startSlot) {
|
2218
|
-
const event = new core.Event(startSlot, null);
|
2219
|
-
core.invokeListener(startSlot.parent, 'onCompositionEnd', event);
|
2138
|
+
const event = new core$1.Event(startSlot, null);
|
2139
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionEnd', event);
|
2220
2140
|
}
|
2221
2141
|
};
|
2222
2142
|
this.subscription.add(stream.fromEvent(input, 'compositionstart').subscribe(() => {
|
@@ -2252,7 +2172,7 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2252
2172
|
if (!range) {
|
2253
2173
|
break;
|
2254
2174
|
}
|
2255
|
-
const location = this.
|
2175
|
+
const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
|
2256
2176
|
const startSlot = this.selection.startSlot;
|
2257
2177
|
if (startSlot) {
|
2258
2178
|
this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
|
@@ -2263,7 +2183,7 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2263
2183
|
case 'insertReplacementText': {
|
2264
2184
|
this.composition = false;
|
2265
2185
|
const range = ev.getTargetRanges()[0];
|
2266
|
-
const location = this.
|
2186
|
+
const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
|
2267
2187
|
const startSlot = this.selection.startSlot;
|
2268
2188
|
this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
|
2269
2189
|
this.commander.delete();
|
@@ -2291,10 +2211,10 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2291
2211
|
this.compositionState = null;
|
2292
2212
|
startIndex = this.selection.startOffset;
|
2293
2213
|
const startSlot = this.selection.startSlot;
|
2294
|
-
const event = new core.Event(startSlot, {
|
2214
|
+
const event = new core$1.Event(startSlot, {
|
2295
2215
|
index: startIndex
|
2296
2216
|
});
|
2297
|
-
core.invokeListener(startSlot.parent, 'onCompositionStart', event);
|
2217
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionStart', event);
|
2298
2218
|
}), stream.fromEvent(input, 'compositionupdate').pipe(stream.filter(() => {
|
2299
2219
|
return !this.ignoreComposition;
|
2300
2220
|
})).subscribe(ev => {
|
@@ -2304,11 +2224,11 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2304
2224
|
index: startIndex,
|
2305
2225
|
data: ev.data
|
2306
2226
|
};
|
2307
|
-
const event = new core.Event(startSlot, {
|
2227
|
+
const event = new core$1.Event(startSlot, {
|
2308
2228
|
index: startIndex,
|
2309
2229
|
data: ev.data
|
2310
2230
|
});
|
2311
|
-
core.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
|
2231
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
|
2312
2232
|
}), stream.merge(stream.fromEvent(input, 'beforeinput').pipe(stream.map(ev => {
|
2313
2233
|
var _a;
|
2314
2234
|
ev.preventDefault();
|
@@ -2317,7 +2237,7 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2317
2237
|
}
|
2318
2238
|
if (ev.inputType === 'insertReplacementText') {
|
2319
2239
|
const range = ev.getTargetRanges()[0];
|
2320
|
-
const location = this.
|
2240
|
+
const location = this.domAdapter.getLocationByNativeNode(range.startContainer);
|
2321
2241
|
const startSlot = this.selection.startSlot;
|
2322
2242
|
this.selection.setBaseAndExtent(startSlot, location.startIndex + range.startOffset, startSlot, location.startIndex + range.endOffset);
|
2323
2243
|
this.commander.delete();
|
@@ -2360,576 +2280,74 @@ exports.NativeInput = class NativeInput extends Input {
|
|
2360
2280
|
if (isCompositionEnd) {
|
2361
2281
|
const startSlot = this.selection.startSlot;
|
2362
2282
|
if (startSlot) {
|
2363
|
-
const event = new core.Event(startSlot, null);
|
2364
|
-
core.invokeListener(startSlot.parent, 'onCompositionEnd', event);
|
2283
|
+
const event = new core$1.Event(startSlot, null);
|
2284
|
+
core$1.invokeListener(startSlot.parent, 'onCompositionEnd', event);
|
2365
2285
|
}
|
2366
2286
|
}
|
2367
2287
|
isCompositionEnd = false;
|
2368
2288
|
}));
|
2369
2289
|
}
|
2370
2290
|
};
|
2371
|
-
|
2372
|
-
|
2373
|
-
__metadata("design:paramtypes", [
|
2291
|
+
NativeInput = __decorate([
|
2292
|
+
core.Injectable(),
|
2293
|
+
__metadata("design:paramtypes", [core.Injector,
|
2374
2294
|
exports.Parser,
|
2375
|
-
core.Scheduler,
|
2376
|
-
core.Selection,
|
2377
|
-
core.Keyboard,
|
2378
|
-
|
2379
|
-
core.Commander,
|
2380
|
-
core.Controller])
|
2381
|
-
],
|
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), ' ');
|
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
|
-
'<': '<',
|
2451
|
-
'>': '>',
|
2452
|
-
'&': '&'
|
2453
|
-
}[str];
|
2454
|
-
});
|
2455
|
-
},
|
2456
|
-
attrName(text) {
|
2457
|
-
return text.replace(/[><"'&]/g, str => {
|
2458
|
-
return {
|
2459
|
-
'<': '<',
|
2460
|
-
'>': '>',
|
2461
|
-
'"': '"',
|
2462
|
-
'\'': ''',
|
2463
|
-
'&': '&'
|
2464
|
-
}[str];
|
2465
|
-
});
|
2466
|
-
},
|
2467
|
-
attrValue(text) {
|
2468
|
-
return text.replace(/["']/g, str => {
|
2469
|
-
return {
|
2470
|
-
'"': '"',
|
2471
|
-
'\'': '''
|
2472
|
-
}[str];
|
2473
|
-
});
|
2474
|
-
}
|
2475
|
-
};
|
2476
|
-
exports.OutputTranslator = OutputTranslator_1 = __decorate([
|
2477
|
-
di.Injectable()
|
2478
|
-
], exports.OutputTranslator);
|
2295
|
+
core$1.Scheduler,
|
2296
|
+
core$1.Selection,
|
2297
|
+
core$1.Keyboard,
|
2298
|
+
DomAdapter,
|
2299
|
+
core$1.Commander,
|
2300
|
+
core$1.Controller])
|
2301
|
+
], NativeInput);
|
2479
2302
|
|
2480
|
-
|
2481
|
-
|
2482
|
-
|
2483
|
-
|
2484
|
-
|
2485
|
-
|
2486
|
-
|
2487
|
-
|
2488
|
-
|
2489
|
-
this.
|
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 = [{
|
2303
|
+
class BrowserModule {
|
2304
|
+
constructor(host, config) {
|
2305
|
+
this.host = host;
|
2306
|
+
this.config = config;
|
2307
|
+
const { mask, wrapper } = BrowserModule.createLayout();
|
2308
|
+
wrapper.prepend(config.adapter.host);
|
2309
|
+
if (config.minHeight) {
|
2310
|
+
config.adapter.host.style.minHeight = config.minHeight;
|
2311
|
+
}
|
2312
|
+
this.providers = [{
|
2498
2313
|
provide: EDITOR_OPTIONS,
|
2499
|
-
useValue:
|
2314
|
+
useValue: config
|
2500
2315
|
}, {
|
2501
2316
|
provide: VIEW_CONTAINER,
|
2502
2317
|
useValue: wrapper
|
2503
2318
|
}, {
|
2504
2319
|
provide: VIEW_DOCUMENT,
|
2505
|
-
useValue:
|
2320
|
+
useValue: config.adapter.host
|
2506
2321
|
}, {
|
2507
2322
|
provide: VIEW_MASK,
|
2508
2323
|
useValue: mask
|
2509
2324
|
}, {
|
2510
|
-
provide: core.
|
2511
|
-
useExisting: exports.DomRenderer
|
2512
|
-
}, {
|
2513
|
-
provide: core.NativeSelectionBridge,
|
2325
|
+
provide: core$1.NativeSelectionBridge,
|
2514
2326
|
useExisting: exports.SelectionBridge
|
2515
2327
|
}, {
|
2516
2328
|
provide: Input,
|
2517
|
-
useClass:
|
2329
|
+
useClass: config.useContentEditable ? NativeInput : exports.MagicInput
|
2518
2330
|
}, {
|
2519
|
-
provide:
|
2520
|
-
useFactory
|
2521
|
-
|
2522
|
-
|
2523
|
-
|
2524
|
-
|
2525
|
-
|
2526
|
-
|
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
|
2331
|
+
provide: core$1.ViewAdapter,
|
2332
|
+
useFactory(v) {
|
2333
|
+
return v;
|
2334
|
+
},
|
2335
|
+
deps: [DomAdapter]
|
2336
|
+
}, {
|
2337
|
+
provide: DomAdapter,
|
2338
|
+
useValue: config.adapter
|
2869
2339
|
},
|
2870
|
-
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
2340
|
+
exports.Parser,
|
2341
|
+
exports.SelectionBridge,
|
2342
|
+
exports.CollaborateCursor
|
2343
|
+
];
|
2344
|
+
this.workbench = wrapper;
|
2345
|
+
this.host.append(wrapper);
|
2874
2346
|
}
|
2875
|
-
|
2876
|
-
|
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
|
-
});
|
2347
|
+
onDestroy() {
|
2348
|
+
this.workbench.remove();
|
2915
2349
|
}
|
2916
|
-
static createLayout(
|
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
|
-
});
|
2350
|
+
static createLayout() {
|
2933
2351
|
const mask = createElement('div', {
|
2934
2352
|
attrs: {
|
2935
2353
|
'data-textbus-view': VIEW_MASK,
|
@@ -2955,29 +2373,23 @@ class Viewer extends core.Starter {
|
|
2955
2373
|
position: 'relative',
|
2956
2374
|
flexDirection: 'column'
|
2957
2375
|
},
|
2958
|
-
children: [
|
2376
|
+
children: [mask]
|
2959
2377
|
});
|
2960
2378
|
return {
|
2961
2379
|
wrapper,
|
2962
|
-
doc,
|
2963
2380
|
mask
|
2964
2381
|
};
|
2965
2382
|
}
|
2966
|
-
static cssMin(str) {
|
2967
|
-
return str
|
2968
|
-
.replace(/\s*(?=[>{}:;,[])/g, '')
|
2969
|
-
.replace(/([>{}:;,])\s*/g, '$1')
|
2970
|
-
.replace(/;}/g, '}').replace(/\s+/, ' ').trim();
|
2971
|
-
}
|
2972
2383
|
}
|
2973
2384
|
|
2385
|
+
exports.BrowserModule = BrowserModule;
|
2974
2386
|
exports.CollaborateSelectionAwarenessDelegate = CollaborateSelectionAwarenessDelegate;
|
2387
|
+
exports.DomAdapter = DomAdapter;
|
2975
2388
|
exports.EDITOR_OPTIONS = EDITOR_OPTIONS;
|
2976
2389
|
exports.Input = Input;
|
2977
2390
|
exports.VIEW_CONTAINER = VIEW_CONTAINER;
|
2978
2391
|
exports.VIEW_DOCUMENT = VIEW_DOCUMENT;
|
2979
2392
|
exports.VIEW_MASK = VIEW_MASK;
|
2980
|
-
exports.Viewer = Viewer;
|
2981
2393
|
exports.createElement = createElement;
|
2982
2394
|
exports.createTextNode = createTextNode;
|
2983
2395
|
exports.getLayoutRectByRange = getLayoutRectByRange;
|