tosijs-styled-editor 0.5.0 → 0.5.2
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/CHANGELOG.md +67 -0
- package/SECURITY.md +29 -0
- package/dist/changes.d.ts +20 -0
- package/dist/index.js +5 -5
- package/dist/module.js +54 -49
- package/dist/tosijs-styled-editor.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
package/dist/module.js
CHANGED
|
@@ -205,10 +205,10 @@ function caretGeometryAt(marker, root) {
|
|
|
205
205
|
return null;
|
|
206
206
|
};
|
|
207
207
|
const caretAt = (node, offset) => {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const rect =
|
|
208
|
+
const range = document.createRange();
|
|
209
|
+
range.setStart(node, offset);
|
|
210
|
+
range.collapse(true);
|
|
211
|
+
const rect = range.getBoundingClientRect();
|
|
212
212
|
return rect.height ? { left: rect.left, top: rect.top, height: rect.height } : null;
|
|
213
213
|
};
|
|
214
214
|
const after = nextText(nextLeafNode(marker, root));
|
|
@@ -314,9 +314,9 @@ function wordBoundsAt(text, offset) {
|
|
|
314
314
|
if (Segmenter) {
|
|
315
315
|
const segmenter = new Segmenter(undefined, { granularity: "word" });
|
|
316
316
|
for (const piece of segmenter.segment(text)) {
|
|
317
|
-
const
|
|
318
|
-
if (offset >= piece.index && offset <
|
|
319
|
-
return { start: piece.index, end
|
|
317
|
+
const end = piece.index + piece.segment.length;
|
|
318
|
+
if (offset >= piece.index && offset < end) {
|
|
319
|
+
return { start: piece.index, end };
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
return { start: offset, end: offset };
|
|
@@ -1724,12 +1724,16 @@ function defineChanges() {
|
|
|
1724
1724
|
if (!customElements.get(DEL_TAG))
|
|
1725
1725
|
customElements.define(DEL_TAG, TosiDel);
|
|
1726
1726
|
}
|
|
1727
|
+
function safeAttributeValue(value) {
|
|
1728
|
+
return value.replace(/[<>]/g, "");
|
|
1729
|
+
}
|
|
1727
1730
|
function mark(kind, text, author) {
|
|
1728
1731
|
const el = document.createElement(kind === "insert" ? INS_TAG : DEL_TAG);
|
|
1729
1732
|
el.setAttribute("data-change", changeId());
|
|
1730
|
-
el.setAttribute("data-author", author.id);
|
|
1731
|
-
if (author.name)
|
|
1732
|
-
el.setAttribute("data-author-name", author.name);
|
|
1733
|
+
el.setAttribute("data-author", safeAttributeValue(author.id));
|
|
1734
|
+
if (author.name) {
|
|
1735
|
+
el.setAttribute("data-author-name", safeAttributeValue(author.name));
|
|
1736
|
+
}
|
|
1733
1737
|
el.setAttribute("data-time", new Date().toISOString());
|
|
1734
1738
|
el.textContent = text;
|
|
1735
1739
|
return el;
|
|
@@ -1773,19 +1777,19 @@ function diffWords(before, after) {
|
|
|
1773
1777
|
const a = tokenize(before);
|
|
1774
1778
|
const b = tokenize(after);
|
|
1775
1779
|
if (a.length > MAX_DIFF_TOKENS || b.length > MAX_DIFF_TOKENS) {
|
|
1776
|
-
const
|
|
1780
|
+
const ops = [];
|
|
1777
1781
|
if (before)
|
|
1778
|
-
|
|
1782
|
+
ops.push({ op: "delete", text: before });
|
|
1779
1783
|
if (after)
|
|
1780
|
-
|
|
1781
|
-
return
|
|
1784
|
+
ops.push({ op: "insert", text: after });
|
|
1785
|
+
return ops;
|
|
1782
1786
|
}
|
|
1783
1787
|
const n = a.length;
|
|
1784
1788
|
const m = b.length;
|
|
1785
1789
|
const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
1786
|
-
for (let
|
|
1787
|
-
for (let
|
|
1788
|
-
lcs[
|
|
1790
|
+
for (let i = n - 1;i >= 0; i--) {
|
|
1791
|
+
for (let j = m - 1;j >= 0; j--) {
|
|
1792
|
+
lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
|
1789
1793
|
}
|
|
1790
1794
|
}
|
|
1791
1795
|
const ops = [];
|
|
@@ -1919,10 +1923,10 @@ function markHits(hits) {
|
|
|
1919
1923
|
const range = document.createRange();
|
|
1920
1924
|
range.setStart(node, start);
|
|
1921
1925
|
range.setEnd(node, Math.min(end, node.data.length));
|
|
1922
|
-
const
|
|
1926
|
+
const mark = document.createElement(MISSPELLING_TAG);
|
|
1923
1927
|
try {
|
|
1924
|
-
range.surroundContents(
|
|
1925
|
-
marked.push(
|
|
1928
|
+
range.surroundContents(mark);
|
|
1929
|
+
marked.push(mark);
|
|
1926
1930
|
} catch {}
|
|
1927
1931
|
}
|
|
1928
1932
|
return marked.reverse();
|
|
@@ -2737,9 +2741,9 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
2737
2741
|
defineChanges();
|
|
2738
2742
|
const ins = document.createElement(INS_TAG);
|
|
2739
2743
|
ins.setAttribute("data-change", changeId());
|
|
2740
|
-
ins.setAttribute("data-author", this.changeAuthor.id);
|
|
2744
|
+
ins.setAttribute("data-author", safeAttributeValue(this.changeAuthor.id));
|
|
2741
2745
|
if (this.changeAuthor.name) {
|
|
2742
|
-
ins.setAttribute("data-author-name", this.changeAuthor.name);
|
|
2746
|
+
ins.setAttribute("data-author-name", safeAttributeValue(this.changeAuthor.name));
|
|
2743
2747
|
}
|
|
2744
2748
|
ins.setAttribute("data-session", this.sessionId);
|
|
2745
2749
|
ins.setAttribute("data-time", new Date().toISOString());
|
|
@@ -2783,9 +2787,9 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
2783
2787
|
}
|
|
2784
2788
|
const del = document.createElement(DEL_TAG);
|
|
2785
2789
|
del.setAttribute("data-change", id);
|
|
2786
|
-
del.setAttribute("data-author", this.changeAuthor.id);
|
|
2790
|
+
del.setAttribute("data-author", safeAttributeValue(this.changeAuthor.id));
|
|
2787
2791
|
if (this.changeAuthor.name) {
|
|
2788
|
-
del.setAttribute("data-author-name", this.changeAuthor.name);
|
|
2792
|
+
del.setAttribute("data-author-name", safeAttributeValue(this.changeAuthor.name));
|
|
2789
2793
|
}
|
|
2790
2794
|
del.setAttribute("data-session", this.sessionId);
|
|
2791
2795
|
del.setAttribute("data-time", new Date().toISOString());
|
|
@@ -3448,15 +3452,15 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
3448
3452
|
}
|
|
3449
3453
|
groupByLine(spans) {
|
|
3450
3454
|
const lineMap = new Map;
|
|
3451
|
-
for (const
|
|
3452
|
-
const rect =
|
|
3455
|
+
for (const span of spans) {
|
|
3456
|
+
const rect = span.getBoundingClientRect();
|
|
3453
3457
|
const top = Math.round(rect.top);
|
|
3454
3458
|
let line = lineMap.get(top);
|
|
3455
3459
|
if (!line) {
|
|
3456
3460
|
line = [];
|
|
3457
3461
|
lineMap.set(top, line);
|
|
3458
3462
|
}
|
|
3459
|
-
line.push(
|
|
3463
|
+
line.push(span);
|
|
3460
3464
|
}
|
|
3461
3465
|
const sortedKeys = Array.from(lineMap.keys()).sort((a, b) => a - b);
|
|
3462
3466
|
return sortedKeys.map((k) => lineMap.get(k));
|
|
@@ -3464,27 +3468,27 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
3464
3468
|
closestCharOnLine(line, targetX) {
|
|
3465
3469
|
let best = line[0];
|
|
3466
3470
|
let bestDist = Infinity;
|
|
3467
|
-
for (const
|
|
3468
|
-
const rect =
|
|
3471
|
+
for (const span of line) {
|
|
3472
|
+
const rect = span.getBoundingClientRect();
|
|
3469
3473
|
const mid = rect.left + rect.width / 2;
|
|
3470
3474
|
const dist = Math.abs(mid - targetX);
|
|
3471
3475
|
if (dist < bestDist) {
|
|
3472
3476
|
bestDist = dist;
|
|
3473
|
-
best =
|
|
3477
|
+
best = span;
|
|
3474
3478
|
}
|
|
3475
3479
|
}
|
|
3476
3480
|
return best;
|
|
3477
3481
|
}
|
|
3478
|
-
positionAtSpanChar(
|
|
3479
|
-
const rect =
|
|
3482
|
+
positionAtSpanChar(span, targetX, extendSelection, blockToDespanify) {
|
|
3483
|
+
const rect = span.getBoundingClientRect();
|
|
3480
3484
|
const start = this.selectable.find(".sel-start");
|
|
3481
3485
|
const end = this.selectable.find(".sel-end");
|
|
3482
3486
|
if (!start || !end)
|
|
3483
3487
|
return;
|
|
3484
3488
|
if (targetX < rect.left + rect.width / 2) {
|
|
3485
|
-
|
|
3489
|
+
span.before(end);
|
|
3486
3490
|
} else {
|
|
3487
|
-
|
|
3491
|
+
span.after(end);
|
|
3488
3492
|
}
|
|
3489
3493
|
if (!extendSelection) {
|
|
3490
3494
|
end.parentNode?.insertBefore(start, end);
|
|
@@ -3548,10 +3552,10 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
3548
3552
|
this.positionAtSpanChar(targetChar, targetX, extendSelection, siblingLi);
|
|
3549
3553
|
return;
|
|
3550
3554
|
}
|
|
3551
|
-
const
|
|
3552
|
-
if (!
|
|
3555
|
+
const sibling = direction === "up" ? currentBlock.previousElementSibling : currentBlock.nextElementSibling;
|
|
3556
|
+
if (!sibling)
|
|
3553
3557
|
return;
|
|
3554
|
-
this.moveVerticalToBlock(
|
|
3558
|
+
this.moveVerticalToBlock(sibling, direction, targetX, extendSelection);
|
|
3555
3559
|
return;
|
|
3556
3560
|
}
|
|
3557
3561
|
const sibling = direction === "up" ? currentBlock.previousElementSibling : currentBlock.nextElementSibling;
|
|
@@ -3935,9 +3939,9 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
3935
3939
|
el.removeAttribute("draggable");
|
|
3936
3940
|
el.removeAttribute("data-drag");
|
|
3937
3941
|
}
|
|
3938
|
-
for (const
|
|
3939
|
-
if (
|
|
3940
|
-
|
|
3942
|
+
for (const span of Array.from(scratch.querySelectorAll("span"))) {
|
|
3943
|
+
if (span.attributes.length === 0) {
|
|
3944
|
+
span.replaceWith(...Array.from(span.childNodes));
|
|
3941
3945
|
}
|
|
3942
3946
|
}
|
|
3943
3947
|
scratch.normalize();
|
|
@@ -4261,17 +4265,17 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
4261
4265
|
if (!marks.length)
|
|
4262
4266
|
return;
|
|
4263
4267
|
const id = changeId();
|
|
4264
|
-
for (const
|
|
4265
|
-
|
|
4266
|
-
|
|
4268
|
+
for (const mark of Array.from(marks)) {
|
|
4269
|
+
mark.setAttribute("data-change", id);
|
|
4270
|
+
mark.setAttribute("data-author", safeAttributeValue(this.changeAuthor.id));
|
|
4267
4271
|
if (this.changeAuthor.name) {
|
|
4268
|
-
|
|
4272
|
+
mark.setAttribute("data-author-name", safeAttributeValue(this.changeAuthor.name));
|
|
4269
4273
|
} else {
|
|
4270
|
-
|
|
4274
|
+
mark.removeAttribute("data-author-name");
|
|
4271
4275
|
}
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4276
|
+
mark.setAttribute("data-session", this.sessionId);
|
|
4277
|
+
mark.setAttribute("data-time", new Date().toISOString());
|
|
4278
|
+
mark.removeAttribute("data-block-delete");
|
|
4275
4279
|
}
|
|
4276
4280
|
}
|
|
4277
4281
|
handlePaste = (evt) => {
|
|
@@ -4730,7 +4734,7 @@ class TosijsStyledEditor extends WebComponent {
|
|
|
4730
4734
|
defineFootnote();
|
|
4731
4735
|
var tosijsStyledEditor = TosijsStyledEditor.elementCreator();
|
|
4732
4736
|
// src/version.ts
|
|
4733
|
-
var version = "0.5.
|
|
4737
|
+
var version = "0.5.2";
|
|
4734
4738
|
export {
|
|
4735
4739
|
DEL_TAG,
|
|
4736
4740
|
FOOTNOTE_TAG,
|
|
@@ -4806,6 +4810,7 @@ export {
|
|
|
4806
4810
|
renumberFootnotes,
|
|
4807
4811
|
rowOfCell,
|
|
4808
4812
|
runCommand,
|
|
4813
|
+
safeAttributeValue,
|
|
4809
4814
|
sanitizeInPlace,
|
|
4810
4815
|
setColumnWidths,
|
|
4811
4816
|
siblingOrder,
|
|
@@ -349,7 +349,7 @@ export declare class TosijsStyledEditor extends WebComponent<EditableParts> {
|
|
|
349
349
|
* not covered.
|
|
350
350
|
*/
|
|
351
351
|
sanitize: (root: Element) => void;
|
|
352
|
-
content: (
|
|
352
|
+
content: (HTMLDivElement | HTMLSlotElement)[];
|
|
353
353
|
formResetCallback(): void;
|
|
354
354
|
connectedCallback(): void;
|
|
355
355
|
/** Reflect whether a slot has assigned content onto the host, and keep it current */
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.5.
|
|
1
|
+
export declare const version = "0.5.2";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tosijs-styled-editor",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Rich text editor web component
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "Rich text editor web component \u2014 no contentEditable, no browser selection APIs",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/tonioloewald/tosijs-editor.git"
|