@libs-ui/components-preview-text-data 0.2.357-2 → 0.2.357-21
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/README.md +327 -187
- package/editor/editor-completions.d.ts +8 -0
- package/editor/editor-forbidden.d.ts +8 -0
- package/editor/editor-height.d.ts +21 -0
- package/esm2022/editor/editor-completions.mjs +36 -0
- package/esm2022/editor/editor-forbidden.mjs +65 -0
- package/esm2022/editor/editor-height.mjs +136 -0
- package/esm2022/linters/js-linter.mjs +348 -0
- package/esm2022/linters/json-linter.mjs +14 -0
- package/esm2022/linters/linter-shared.mjs +8 -0
- package/esm2022/linters/python-linter.mjs +52 -0
- package/esm2022/linters/sql-linter.mjs +34 -0
- package/esm2022/preview-text-data.component.mjs +43 -319
- package/esm2022/preview-text-data.define.mjs +13 -1
- package/esm2022/preview-text-data.type.mjs +2 -0
- package/fesm2022/libs-ui-components-preview-text-data.mjs +733 -320
- package/fesm2022/libs-ui-components-preview-text-data.mjs.map +1 -1
- package/linters/js-linter.d.ts +45 -0
- package/linters/json-linter.d.ts +4 -0
- package/linters/linter-shared.d.ts +3 -0
- package/linters/python-linter.d.ts +4 -0
- package/linters/sql-linter.d.ts +4 -0
- package/package.json +9 -7
- package/preview-text-data.component.d.ts +20 -35
- package/preview-text-data.define.d.ts +12 -0
- package/preview-text-data.type.d.ts +33 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { syntaxTree } from '@codemirror/language';
|
|
2
|
+
import { linter } from '@codemirror/lint';
|
|
3
|
+
import { EditorState, Transaction } from '@codemirror/state';
|
|
4
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
5
|
+
const toGlobalRegex = (pattern) => {
|
|
6
|
+
if (pattern instanceof RegExp) {
|
|
7
|
+
const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
|
|
8
|
+
return new RegExp(pattern.source, flags);
|
|
9
|
+
}
|
|
10
|
+
return new RegExp(escapeRegex(pattern), 'gi');
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Extension bảo mật gồm 2 layer:
|
|
14
|
+
* 1. Linter: highlight đỏ tại đúng vị trí match (bỏ qua match trong comment) + gọi `emitViolations`.
|
|
15
|
+
* 2. Transaction filter: chặn paste nếu nội dung paste chứa pattern cấm.
|
|
16
|
+
*/
|
|
17
|
+
export const buildForbiddenExtension = (patterns, emitViolations) => {
|
|
18
|
+
const forbiddenLinter = linter((view) => {
|
|
19
|
+
const diagnostics = [];
|
|
20
|
+
const violations = [];
|
|
21
|
+
const text = view.state.doc.toString();
|
|
22
|
+
const commentRanges = [];
|
|
23
|
+
syntaxTree(view.state).iterate({
|
|
24
|
+
enter: (node) => {
|
|
25
|
+
if (node.type.name.toLowerCase().includes('comment')) {
|
|
26
|
+
commentRanges.push({ from: node.from, to: node.to });
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
const isInComment = (from, to) => commentRanges.some((r) => from >= r.from && to <= r.to);
|
|
31
|
+
for (const pattern of patterns) {
|
|
32
|
+
const re = toGlobalRegex(pattern);
|
|
33
|
+
re.lastIndex = 0;
|
|
34
|
+
let match;
|
|
35
|
+
while ((match = re.exec(text)) !== null) {
|
|
36
|
+
const from = match.index;
|
|
37
|
+
const to = from + match[0].length;
|
|
38
|
+
if (isInComment(from, to))
|
|
39
|
+
continue;
|
|
40
|
+
const line = view.state.doc.lineAt(from).number;
|
|
41
|
+
const patternLabel = pattern instanceof RegExp ? pattern.source : pattern;
|
|
42
|
+
diagnostics.push({ from, to, severity: 'error', message: `Mã bị cấm: "${match[0]}" — vi phạm chính sách bảo mật` });
|
|
43
|
+
violations.push({ from, to, line, pattern: patternLabel, matched: match[0] });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
emitViolations(violations);
|
|
47
|
+
return diagnostics;
|
|
48
|
+
});
|
|
49
|
+
const pasteFilter = EditorState.transactionFilter.of((tr) => {
|
|
50
|
+
if (!tr.docChanged)
|
|
51
|
+
return tr;
|
|
52
|
+
if (!tr.annotation(Transaction.userEvent)?.startsWith('input.paste'))
|
|
53
|
+
return tr;
|
|
54
|
+
const newText = tr.newDoc.toString();
|
|
55
|
+
for (const pattern of patterns) {
|
|
56
|
+
const re = toGlobalRegex(pattern);
|
|
57
|
+
re.lastIndex = 0;
|
|
58
|
+
if (re.test(newText))
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
return tr;
|
|
62
|
+
});
|
|
63
|
+
return [forbiddenLinter, pasteFilter];
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRpdG9yLWZvcmJpZGRlbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcmV2aWV3LXRleHQtZGF0YS9zcmMvZWRpdG9yL2VkaXRvci1mb3JiaWRkZW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2xELE9BQU8sRUFBYyxNQUFNLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUsV0FBVyxFQUFhLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBSXhFLE1BQU0sV0FBVyxHQUFHLENBQUMsR0FBVyxFQUFVLEVBQUUsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBRXhGLE1BQU0sYUFBYSxHQUFHLENBQUMsT0FBMkIsRUFBVSxFQUFFO0lBQzVELElBQUksT0FBTyxZQUFZLE1BQU0sRUFBRSxDQUFDO1FBQzlCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxLQUFLLEdBQUcsQ0FBQztRQUNoRixPQUFPLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUNELE9BQU8sSUFBSSxNQUFNLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ2hELENBQUMsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLFFBQThCLEVBQUUsY0FBaUUsRUFBYSxFQUFFO0lBQ3RKLE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFB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QUFPLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNyQyxLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQy9CLE1BQU0sRUFBRSxHQUFHLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNsQyxFQUFFLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQztZQUNqQixJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxDQUFDO1FBQ2xDLENBQUM7UUFDRCxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUMsQ0FBQyxDQUFDO0lBRUgsT0FBTyxDQUFDLGVBQWUsRUFBRSxXQUFXLENBQUMsQ0FBQztBQUN4QyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBzeW50YXhUcmVlIH0gZnJvbSAnQGNvZGVtaXJyb3IvbGFuZ3VhZ2UnO1xuaW1wb3J0IHsgRGlhZ25vc3RpYywgbGludGVyIH0gZnJvbSAnQGNvZGVtaXJyb3IvbGludCc7XG5pbXBvcnQgeyBFZGl0b3JTdGF0ZSwgRXh0ZW5zaW9uLCBUcmFuc2FjdGlvbiB9IGZyb20gJ0Bjb2RlbWlycm9yL3N0YXRlJztcbmltcG9ydCB0eXBlIHsgU3ludGF4Tm9kZVJlZiB9IGZyb20gJ0BsZXplci9jb21tb24nO1xuaW1wb3J0IHsgSVByZXZpZXdUZXh0RGF0YVZpb2xhdGlvbiwgVF9Gb3JiaWRkZW5QYXR0ZXJuIH0gZnJvbSAnLi4vcHJldmlldy10ZXh0LWRhdGEuaW50ZXJmYWNlcyc7XG5cbmNvbnN0IGVzY2FwZVJlZ2V4ID0gKHN0cjogc3RyaW5nKTogc3RyaW5nID0+IHN0ci5yZXBsYWNlKC9bLiorP14ke30oKXxbXFxdXFxcXF0vZywgJ1xcXFwkJicpO1xuXG5jb25zdCB0b0dsb2JhbFJlZ2V4ID0gKHBhdHRlcm46IFRfRm9yYmlkZGVuUGF0dGVybik6IFJlZ0V4cCA9PiB7XG4gIGlmIChwYXR0ZXJuIGluc3RhbmNlb2YgUmVnRXhwKSB7XG4gICAgY29uc3QgZmxhZ3MgPSBwYXR0ZXJuLmZsYWdzLmluY2x1ZGVzKCdnJykgPyBwYXR0ZXJuLmZsYWdzIDogYCR7cGF0dGVybi5mbGFnc31nYDtcbiAgICByZXR1cm4gbmV3IFJlZ0V4cChwYXR0ZXJuLnNvdXJjZSwgZmxhZ3MpO1xuICB9XG4gIHJldHVybiBuZXcgUmVnRXhwKGVzY2FwZVJlZ2V4KHBhdHRlcm4pLCAnZ2knKTtcbn07XG5cbi8qKlxuICogRXh0ZW5zaW9uIGLhuqNvIG3huq10IGfhu5NtIDIgbGF5ZXI6XG4gKiAxLiBMaW50ZXI6IGhpZ2hsaWdodCDEkeG7jyB04bqhaSDEkcO6bmcgduG7iyB0csOtIG1hdGNoIChi4buPIHF1YSBtYXRjaCB0cm9uZyBjb21tZW50KSArIGfhu41pIGBlbWl0VmlvbGF0aW9uc2AuXG4gKiAyLiBUcmFuc2FjdGlvbiBmaWx0ZXI6IGNo4bq3biBwYXN0ZSBu4bq/dSBu4buZaSBkdW5nIHBhc3RlIGNo4bupYSBwYXR0ZXJuIGPhuqVtLlxuICovXG5leHBvcnQgY29uc3QgYnVpbGRGb3JiaWRkZW5FeHRlbnNpb24gPSAocGF0dGVybnM6IFRfRm9yYmlkZGVuUGF0dGVybltdLCBlbWl0VmlvbGF0aW9uczogKHZpb2xhdGlvbnM6IElQcmV2aWV3VGV4dERhdGFWaW9sYXRpb25bXSkgPT4gdm9pZCk6IEV4dGVuc2lvbiA9PiB7XG4gIGNvbnN0IGZvcmJpZGRlbkxpbnRlciA9IGxpbnRlcigodmlldykgPT4ge1xuICAgIGNvbnN0IGRpYWdub3N0aWNzOiBEaWFnbm9zdGljW10gPSBbXTtcbiAgICBjb25zdCB2aW9sYXRpb25zOiBJUHJldmlld1RleHREYXRhVmlvbGF0aW9uW10gPSBbXTtcbiAgICBjb25zdCB0ZXh0ID0gdmlldy5zdGF0ZS5kb2MudG9TdHJpbmcoKTtcblxuICAgIGNvbnN0IGNvbW1lbnRSYW5nZXM6IHsgZnJvbTogbnVtYmVyOyB0bzogbnVtYmVyIH1bXSA9IFtdO1xuICAgIHN5bnRheFRyZWUodmlldy5zdGF0ZSkuaXRlcmF0ZSh7XG4gICAgICBlbnRlcjogKG5vZGU6IFN5bnRheE5vZGVSZWYpID0+IHtcbiAgICAgICAgaWYgKG5vZGUudHlwZS5uYW1lLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ2NvbW1lbnQnKSkge1xuICAgICAgICAgIGNvbW1lbnRSYW5nZXMucHVzaCh7IGZyb206IG5vZGUuZnJvbSwgdG86IG5vZGUudG8gfSk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSk7XG4gICAgY29uc3QgaXNJbkNvbW1lbnQgPSAoZnJvbTogbnVtYmVyLCB0bzogbnVtYmVyKTogYm9vbGVhbiA9PiBjb21tZW50UmFuZ2VzLnNvbWUoKHIpID0+IGZyb20gPj0gci5mcm9tICYmIHRvIDw9IHIudG8pO1xuXG4gICAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIHBhdHRlcm5zKSB7XG4gICAgICBjb25zdCByZSA9IHRvR2xvYmFsUmVnZXgocGF0dGVybik7XG4gICAgICByZS5sYXN0SW5kZXggPSAwO1xuICAgICAgbGV0IG1hdGNoOiBSZWdFeHBFeGVjQXJyYXkgfCBudWxsO1xuICAgICAgd2hpbGUgKChtYXRjaCA9IHJlLmV4ZWModGV4dCkpICE9PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IGZyb20gPSBtYXRjaC5pbmRleDtcbiAgICAgICAgY29uc3QgdG8gPSBmcm9tICsgbWF0Y2hbMF0ubGVuZ3RoO1xuICAgICAgICBpZiAoaXNJbkNvbW1lbnQoZnJvbSwgdG8pKSBjb250aW51ZTtcbiAgICAgICAgY29uc3QgbGluZSA9IHZpZXcuc3RhdGUuZG9jLmxpbmVBdChmcm9tKS5udW1iZXI7XG4gICAgICAgIGNvbnN0IHBhdHRlcm5MYWJlbCA9IHBhdHRlcm4gaW5zdGFuY2VvZiBSZWdFeHAgPyBwYXR0ZXJuLnNvdXJjZSA6IHBhdHRlcm47XG4gICAgICAgIGRpYWdub3N0aWNzLnB1c2goeyBmcm9tLCB0bywgc2V2ZXJpdHk6ICdlcnJvcicsIG1lc3NhZ2U6IGBNw6MgYuG7iyBj4bqlbTogXCIke21hdGNoWzBdfVwiIOKAlCB2aSBwaOG6oW0gY2jDrW5oIHPDoWNoIGLhuqNvIG3huq10YCB9KTtcbiAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHsgZnJvbSwgdG8sIGxpbmUsIHBhdHRlcm46IHBhdHRlcm5MYWJlbCwgbWF0Y2hlZDogbWF0Y2hbMF0gfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgZW1pdFZpb2xhdGlvbnModmlvbGF0aW9ucyk7XG4gICAgcmV0dXJuIGRpYWdub3N0aWNzO1xuICB9KTtcblxuICBjb25zdCBwYXN0ZUZpbHRlciA9IEVkaXRvclN0YXRlLnRyYW5zYWN0aW9uRmlsdGVyLm9mKCh0cjogVHJhbnNhY3Rpb24pID0+IHtcbiAgICBpZiAoIXRyLmRvY0NoYW5nZWQpIHJldHVybiB0cjtcbiAgICBpZiAoIXRyLmFubm90YXRpb24oVHJhbnNhY3Rpb24udXNlckV2ZW50KT8uc3RhcnRzV2l0aCgnaW5wdXQucGFzdGUnKSkgcmV0dXJuIHRyO1xuICAgIGNvbnN0IG5ld1RleHQgPSB0ci5uZXdEb2MudG9TdHJpbmcoKTtcbiAgICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YgcGF0dGVybnMpIHtcbiAgICAgIGNvbnN0IHJlID0gdG9HbG9iYWxSZWdleChwYXR0ZXJuKTtcbiAgICAgIHJlLmxhc3RJbmRleCA9IDA7XG4gICAgICBpZiAocmUudGVzdChuZXdUZXh0KSkgcmV0dXJuIFtdO1xuICAgIH1cbiAgICByZXR1cm4gdHI7XG4gIH0pO1xuXG4gIHJldHVybiBbZm9yYmlkZGVuTGludGVyLCBwYXN0ZUZpbHRlcl07XG59O1xuIl19
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/** Parse giá trị CSS px → number. Trả về undefined nếu không phải px hợp lệ (vd `none`, `0`). */
|
|
2
|
+
export const parsePx = (raw) => {
|
|
3
|
+
if (!raw || !raw.endsWith('px')) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const value = parseFloat(raw);
|
|
7
|
+
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Chiều cao MỘT dòng, lấy từ CSS `line-height` của vùng nội dung.
|
|
11
|
+
*
|
|
12
|
+
* 🔴 KHÔNG đo `getBoundingClientRect()` của `.cm-line`: số đó phụ thuộc thời điểm gọi và cho ra
|
|
13
|
+
* hàng loạt giá trị sai khi editor vừa dựng — đo trên modal 08–09/09/2026 với dòng thật `18.19px`
|
|
14
|
+
* đã gặp `5.82` · `11.61` · `15.88` · `24` · `59`, mỗi lần mở một số khác. `defaultLineHeight` của
|
|
15
|
+
* CodeMirror cũng không dùng được vì là số mặc định (20/24px), không theo font sản phẩm.
|
|
16
|
+
*
|
|
17
|
+
* `line-height` trong CSS thì có ngay khi phần tử gắn vào DOM và không đổi theo thời điểm đọc.
|
|
18
|
+
*/
|
|
19
|
+
const readLineHeight = (view) => {
|
|
20
|
+
const style = getComputedStyle(view.contentDOM);
|
|
21
|
+
const lineHeight = parseFloat(style.lineHeight);
|
|
22
|
+
if (Number.isFinite(lineHeight) && lineHeight > 0) {
|
|
23
|
+
return lineHeight;
|
|
24
|
+
}
|
|
25
|
+
// `line-height: normal` → suy ra từ cỡ chữ, hệ số 1.4 là mức CodeMirror dùng cho editor.
|
|
26
|
+
const fontSize = parseFloat(style.fontSize);
|
|
27
|
+
if (Number.isFinite(fontSize) && fontSize > 0) {
|
|
28
|
+
return fontSize * 1.4;
|
|
29
|
+
}
|
|
30
|
+
return view.defaultLineHeight || 20;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Chạy `apply` khi đã CÓ dòng thật để đo.
|
|
34
|
+
*
|
|
35
|
+
* Vì sao không chỉ chờ `document.fonts.ready`: editor có thể dựng SAU lúc font đã nạp (vd ô JSON chỉ
|
|
36
|
+
* hiện khi người dùng bấm chạy thử). Lúc đó `fonts.ready` giải quyết ngay nhưng `.cm-line` chưa
|
|
37
|
+
* render, phép đo rơi vào `defaultLineHeight` — đo 08/09/2026: khung JSON thành 895px (48 dòng)
|
|
38
|
+
* thay vì 281px. Nên: chờ cả font VÀ dòng đầu tiên xuất hiện, thử lại tối đa `SO_LAN_THU` nhịp.
|
|
39
|
+
*/
|
|
40
|
+
const SO_LAN_THU_DO = 30;
|
|
41
|
+
const runWhenFontReady = (view, apply) => {
|
|
42
|
+
const fonts = typeof document === 'undefined' ? undefined : document.fonts;
|
|
43
|
+
const doKhiSanSang = (conLai) => {
|
|
44
|
+
// Chỉ cần vùng nội dung đã gắn vào DOM là `line-height` đọc được — không cần chờ vẽ dòng.
|
|
45
|
+
if (view.contentDOM.isConnected) {
|
|
46
|
+
apply();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (conLai <= 0) {
|
|
50
|
+
// Hết nhịp chờ vẫn chưa có dòng thật — đo bằng số dự phòng, thà lệch hơn không chặn chiều cao.
|
|
51
|
+
apply();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
requestAnimationFrame(() => doKhiSanSang(conLai - 1));
|
|
55
|
+
};
|
|
56
|
+
if (!fonts?.ready) {
|
|
57
|
+
doKhiSanSang(SO_LAN_THU_DO);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
fonts.ready.then(() => doKhiSanSang(SO_LAN_THU_DO)).catch(() => doKhiSanSang(SO_LAN_THU_DO));
|
|
61
|
+
};
|
|
62
|
+
/** Bù dòng trống cho đủ `minLines` (xử lý 1 lần khi khởi tạo). Đủ rồi giữ nguyên, không cắt bớt. */
|
|
63
|
+
export const applyMinLines = (content, minLines) => {
|
|
64
|
+
const value = content ?? '';
|
|
65
|
+
if (!minLines || minLines <= 0) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
const currentLines = value === '' ? 1 : value.split('\n').length;
|
|
69
|
+
if (currentLines >= minLines) {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
return value + '\n'.repeat(minLines - currentLines);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Tính min/max-height vùng cuộn theo CSS của container ngoài, snap về bội số dòng.
|
|
76
|
+
* Trừ offset phần header/toolbar (giữa đỉnh container và đỉnh vùng cuộn) để vừa khít container.
|
|
77
|
+
*/
|
|
78
|
+
export const applyContainerHeight = (view, container) => {
|
|
79
|
+
const style = getComputedStyle(container);
|
|
80
|
+
const minPx = parsePx(style.minHeight);
|
|
81
|
+
const maxPx = parsePx(style.maxHeight);
|
|
82
|
+
if (minPx === undefined && maxPx === undefined) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
runWhenFontReady(view, () => view.requestMeasure({
|
|
86
|
+
read: (measureView) => {
|
|
87
|
+
const lineHeight = readLineHeight(measureView);
|
|
88
|
+
const offsetTop = Math.max(measureView.scrollDOM.getBoundingClientRect().top - container.getBoundingClientRect().top, 0);
|
|
89
|
+
const toLines = (px) => Math.max(1, Math.floor((px - offsetTop - 8) / lineHeight));
|
|
90
|
+
return { lineHeight, minLines: minPx === undefined ? undefined : toLines(minPx), maxLines: maxPx === undefined ? undefined : toLines(maxPx) };
|
|
91
|
+
},
|
|
92
|
+
write: ({ lineHeight, minLines, maxLines }, writeView) => {
|
|
93
|
+
if (maxLines !== undefined) {
|
|
94
|
+
writeView.scrollDOM.style.maxHeight = `${maxLines * lineHeight + 8}px`;
|
|
95
|
+
writeView.scrollDOM.style.overflowY = 'auto';
|
|
96
|
+
}
|
|
97
|
+
// min-height: bù dòng trống vào content (hiện đủ line-number 1..N), giống cơ chế minLines.
|
|
98
|
+
if (minLines !== undefined) {
|
|
99
|
+
const currentLines = writeView.state.doc.lines;
|
|
100
|
+
if (currentLines < minLines) {
|
|
101
|
+
queueMicrotask(() => writeView.dispatch({ changes: { from: writeView.state.doc.length, insert: '\n'.repeat(minLines - currentLines) } }));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
}));
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Giới hạn chiều cao editor (xử lý 1 lần). 2 chế độ loại trừ nhau:
|
|
109
|
+
* - `maxLines`: cap theo số dòng truyền vào.
|
|
110
|
+
* - `container`: tự tính số dòng theo `min-height`/`max-height` (CSS) của container, snap bội số dòng.
|
|
111
|
+
* Cấu hình đồng thời `container` + `minLines`/`maxLines` → throw.
|
|
112
|
+
*/
|
|
113
|
+
export const applyMaxHeight = (view, options) => {
|
|
114
|
+
const { minLines, maxLines, container } = options;
|
|
115
|
+
if (container && (minLines || maxLines)) {
|
|
116
|
+
throw new Error('[preview-text-data] CẤM cấu hình [containerElement] cùng [minLines]/[maxLines]. Container tự quản lý min/max height.');
|
|
117
|
+
}
|
|
118
|
+
if (!view) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (container) {
|
|
122
|
+
applyContainerHeight(view, container);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!maxLines || maxLines <= 0) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
runWhenFontReady(view, () => view.requestMeasure({
|
|
129
|
+
read: (measureView) => readLineHeight(measureView),
|
|
130
|
+
write: (lineHeight, writeView) => {
|
|
131
|
+
writeView.scrollDOM.style.maxHeight = `${maxLines * lineHeight + 8}px`;
|
|
132
|
+
writeView.scrollDOM.style.overflowY = 'auto';
|
|
133
|
+
},
|
|
134
|
+
}));
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRpdG9yLWhlaWdodC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcmV2aWV3LXRleHQtZGF0YS9zcmMvZWRpdG9yL2VkaXRvci1oZWlnaHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsaUdBQWlHO0FBQ2pHLE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxDQUFDLEdBQVcsRUFBc0IsRUFBRTtJQUN6RCxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDOUIsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ2pFLENBQUMsQ0FBQztBQUVGOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sY0FBYyxHQUFHLENBQUMsSUFBZ0IsRUFBVSxFQUFFO0lBQ2xELE1BQU0sS0FBSyxHQUFHLGdCQUFnQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUNoRCxNQUFNLFVBQVUsR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBRWhELElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsSUFBSSxVQUFVLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEQsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUNELHlGQUF5RjtJQUN6RixNQUFNLFFBQVEsR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBRTVDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxR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QUFFLEVBQUU7WUFDL0IsU0FBUyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsU0FBUyxHQUFHLEdBQUcsUUFBUSxHQUFHLFVBQVUsR0FBRyxDQUFDLElBQUksQ0FBQztZQUN2RSxTQUFTLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDO1FBQy9DLENBQUM7S0FDRixDQUFDLENBQUMsQ0FBQztBQUNOLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEVkaXRvclZpZXcgfSBmcm9tICdAY29kZW1pcnJvci92aWV3JztcblxuLyoqIFBhcnNlIGdpw6EgdHLhu4sgQ1NTIHB4IOKGkiBudW1iZXIuIFRy4bqjIHbhu4EgdW5kZWZpbmVkIG7hur91IGtow7RuZyBwaOG6o2kgcHggaOG7o3AgbOG7hyAodmQgYG5vbmVgLCBgMGApLiAqL1xuZXhwb3J0IGNvbnN0IHBhcnNlUHggPSAocmF3OiBzdHJpbmcpOiBudW1iZXIgfCB1bmRlZmluZWQgPT4ge1xuICBpZiAoIXJhdyB8fCAhcmF3LmVuZHNXaXRoKCdweCcpKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICBjb25zdCB2YWx1ZSA9IHBhcnNlRmxvYXQocmF3KTtcbiAgcmV0dXJuIE51bWJlci5pc0Zpbml0ZSh2YWx1ZSkgJiYgdmFsdWUgPiAwID8gdmFsdWUgOiB1bmRlZmluZWQ7XG59O1xuXG4vKipcbiAqIENoaeG7gXUgY2FvIE3hu5hUIGTDsm5nLCBs4bqleSB04burIENTUyBgbGluZS1oZWlnaHRgIGPhu6dhIHbDuW5nIG7hu5lpIGR1bmcuXG4gKlxuICog8J+UtCBLSMOUTkcgxJFvIGBnZXRCb3VuZGluZ0NsaWVudFJlY3QoKWAgY+G7p2EgYC5jbS1saW5lYDogc+G7kSDEkcOzIHBo4bulIHRodeG7mWMgdGjhu51pIMSRaeG7g20gZ+G7jWkgdsOgIGNobyByYVxuICogaMOgbmcgbG/huqF0IGdpw6EgdHLhu4sgc2FpIGtoaSBlZGl0b3IgduG7q2EgZOG7sW5nIOKAlCDEkW8gdHLDqm4gbW9kYWwgMDjigJMwOS8wOS8yMDI2IHbhu5tpIGTDsm5nIHRo4bqtdCBgMTguMTlweGBcbiAqIMSRw6MgZ+G6t3AgYDUuODJgIMK3IGAxMS42MWAgwrcgYDE1Ljg4YCDCtyBgMjRgIMK3IGA1OWAsIG3hu5dpIGzhuqduIG3hu58gbeG7mXQgc+G7kSBraMOhYy4gYGRlZmF1bHRMaW5lSGVpZ2h0YCBj4bunYVxuICogQ29kZU1pcnJvciBjxaluZyBraMO0bmcgZMO5bmcgxJHGsOG7o2MgdsOsIGzDoCBz4buRIG3hurdjIMSR4buLbmggKDIwLzI0cHgpLCBraMO0bmcgdGhlbyBmb250IHPhuqNuIHBo4bqpbS5cbiAqXG4gKiBgbGluZS1oZWlnaHRgIHRyb25nIENTUyB0aMOsIGPDsyBuZ2F5IGtoaSBwaOG6p24gdOG7rSBn4bqvbiB2w6BvIERPTSB2w6Aga2jDtG5nIMSR4buVaSB0aGVvIHRo4budaSDEkWnhu4NtIMSR4buNYy5cbiAqL1xuY29uc3QgcmVhZExpbmVIZWlnaHQgPSAodmlldzogRWRpdG9yVmlldyk6IG51bWJlciA9PiB7XG4gIGNvbnN0IHN0eWxlID0gZ2V0Q29tcHV0ZWRTdHlsZSh2aWV3LmNvbnRlbnRET00pO1xuICBjb25zdCBsaW5lSGVpZ2h0ID0gcGFyc2VGbG9hdChzdHlsZS5saW5lSGVpZ2h0KTtcblxuICBpZiAoTnVtYmVyLmlzRmluaXRlKGxpbmVIZWlnaHQpICYmIGxpbmVIZWlnaHQgPiAwKSB7XG4gICAgcmV0dXJuIGxpbmVIZWlnaHQ7XG4gIH1cbiAgLy8gYGxpbmUtaGVpZ2h0OiBub3JtYWxgIOKGkiBzdXkgcmEgdOG7qyBj4buhIGNo4buvLCBo4buHIHPhu5EgMS40IGzDoCBt4bupYyBDb2RlTWlycm9yIGTDuW5nIGNobyBlZGl0b3IuXG4gIGNvbnN0IGZvbnRTaXplID0gcGFyc2VGbG9hdChzdHlsZS5mb250U2l6ZSk7XG5cbiAgaWYgKE51bWJlci5pc0Zpbml0ZShmb250U2l6ZSkgJiYgZm9udFNpemUgPiAwKSB7XG4gICAgcmV0dXJuIGZvbnRTaXplICogMS40O1xuICB9XG5cbiAgcmV0dXJuIHZpZXcuZGVmYXVsdExpbmVIZWlnaHQgfHwgMjA7XG59O1xuXG4vKipcbiAqIENo4bqheSBgYXBwbHlgIGtoaSDEkcOjIEPDkyBkw7JuZyB0aOG6rXQgxJHhu4MgxJFvLlxuICpcbiAqIFbDrCBzYW8ga2jDtG5nIGNo4buJIGNo4budIGBkb2N1bWVudC5mb250cy5yZWFkeWA6IGVkaXRvciBjw7MgdGjhu4MgZOG7sW5nIFNBVSBsw7pjIGZvbnQgxJHDoyBu4bqhcCAodmQgw7QgSlNPTiBjaOG7iVxuICogaGnhu4duIGtoaSBuZ8aw4budaSBkw7luZyBi4bqlbSBjaOG6oXkgdGjhu60pLiBMw7pjIMSRw7MgYGZvbnRzLnJlYWR5YCBnaeG6o2kgcXV54bq/dCBuZ2F5IG5oxrBuZyBgLmNtLWxpbmVgIGNoxrBhXG4gKiByZW5kZXIsIHBow6lwIMSRbyByxqFpIHbDoG8gYGRlZmF1bHRMaW5lSGVpZ2h0YCDigJQgxJFvIDA4LzA5LzIwMjY6IGtodW5nIEpTT04gdGjDoG5oIDg5NXB4ICg0OCBkw7JuZylcbiAqIHRoYXkgdsOsIDI4MXB4LiBOw6puOiBjaOG7nSBj4bqjIGZvbnQgVsOAIGTDsm5nIMSR4bqndSB0acOqbiB4deG6pXQgaGnhu4duLCB0aOG7rSBs4bqhaSB04buRaSDEkWEgYFNPX0xBTl9USFVgIG5o4buLcC5cbiAqL1xuY29uc3QgU09fTEFOX1RIVV9ETyA9IDMwO1xuXG5jb25zdCBydW5XaGVuRm9udFJlYWR5ID0gKHZpZXc6IEVkaXRvclZpZXcsIGFwcGx5OiAoKSA9PiB2b2lkKTogdm9pZCA9PiB7XG4gIGNvbnN0IGZvbnRzID0gdHlwZW9mIGRvY3VtZW50ID09PSAndW5kZWZpbmVkJyA/IHVuZGVmaW5lZCA6IGRvY3VtZW50LmZvbnRzO1xuICBjb25zdCBkb0toaVNhblNhbmcgPSAoY29uTGFpOiBudW1iZXIpOiB2b2lkID0+IHtcbiAgICAvLyBDaOG7iSBj4bqnbiB2w7luZyBu4buZaSBkdW5nIMSRw6MgZ+G6r24gdsOgbyBET00gbMOgIGBsaW5lLWhlaWdodGAgxJHhu41jIMSRxrDhu6NjIOKAlCBraMO0bmcgY+G6p24gY2jhu50gduG6vSBkw7JuZy5cbiAgICBpZiAodmlldy5jb250ZW50RE9NLmlzQ29ubmVjdGVkKSB7XG4gICAgICBhcHBseSgpO1xuXG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChjb25MYWkgPD0gMCkge1xuICAgICAgLy8gSOG6v3Qgbmjhu4twIGNo4budIHbhuqtuIGNoxrBhIGPDsyBkw7JuZyB0aOG6rXQg4oCUIMSRbyBi4bqxbmcgc+G7kSBk4buxIHBow7JuZywgdGjDoCBs4buHY2ggaMahbiBraMO0bmcgY2jhurduIGNoaeG7gXUgY2FvLlxuICAgICAgYXBwbHkoKTtcblxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gZG9LaGlTYW5TYW5nKGNvbkxhaSAtIDEpKTtcbiAgfTtcblxuICBpZiAoIWZvbnRzPy5yZWFkeSkge1xuICAgIGRvS2hpU2FuU2FuZyhTT19MQU5fVEhVX0RPKTtcblxuICAgIHJldHVybjtcbiAgfVxuICBmb250cy5yZWFkeS50aGVuKCgpID0+IGRvS2hpU2FuU2FuZyhTT19MQU5fVEhVX0RPKSkuY2F0Y2goKCkgPT4gZG9LaGlTYW5TYW5nKFNPX0xBTl9USFVfRE8pKTtcbn07XG5cbi8qKiBCw7kgZMOybmcgdHLhu5FuZyBjaG8gxJHhu6cgYG1pbkxpbmVzYCAoeOG7rSBsw70gMSBs4bqnbiBraGkga2jhu59pIHThuqFvKS4gxJDhu6cgcuG7k2kgZ2nhu68gbmd1ecOqbiwga2jDtG5nIGPhuq90IGLhu5t0LiAqL1xuZXhwb3J0IGNvbnN0IGFwcGx5TWluTGluZXMgPSAoY29udGVudDogc3RyaW5nLCBtaW5MaW5lcz86IG51bWJlcik6IHN0cmluZyA9PiB7XG4gIGNvbnN0IHZhbHVlID0gY29udGVudCA/PyAnJztcbiAgaWYgKCFtaW5MaW5lcyB8fCBtaW5MaW5lcyA8PSAwKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGNvbnN0IGN1cnJlbnRMaW5lcyA9IHZhbHVlID09PSAnJyA/IDEgOiB2YWx1ZS5zcGxpdCgnXFxuJykubGVuZ3RoO1xuICBpZiAoY3VycmVudExpbmVzID49IG1pbkxpbmVzKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIHJldHVybiB2YWx1ZSArICdcXG4nLnJlcGVhdChtaW5MaW5lcyAtIGN1cnJlbnRMaW5lcyk7XG59O1xuXG4vKipcbiAqIFTDrW5oIG1pbi9tYXgtaGVpZ2h0IHbDuW5nIGN14buZbiB0aGVvIENTUyBj4bunYSBjb250YWluZXIgbmdvw6BpLCBzbmFwIHbhu4EgYuG7mWkgc+G7kSBkw7JuZy5cbiAqIFRy4burIG9mZnNldCBwaOG6p24gaGVhZGVyL3Rvb2xiYXIgKGdp4buvYSDEkeG7iW5oIGNvbnRhaW5lciB2w6AgxJHhu4luaCB2w7luZyBjdeG7mW4pIMSR4buDIHbhu6thIGtow610IGNvbnRhaW5lci5cbiAqL1xuZXhwb3J0IGNvbnN0IGFwcGx5Q29udGFpbmVySGVpZ2h0ID0gKHZpZXc6IEVkaXRvclZpZXcsIGNvbnRhaW5lcjogSFRNTEVsZW1lbnQpOiB2b2lkID0+IHtcbiAgY29uc3Qgc3R5bGUgPSBnZXRDb21wdXRlZFN0eWxlKGNvbnRhaW5lcik7XG4gIGNvbnN0IG1pblB4ID0gcGFyc2VQeChzdHlsZS5taW5IZWlnaHQpO1xuICBjb25zdCBtYXhQeCA9IHBhcnNlUHgoc3R5bGUubWF4SGVpZ2h0KTtcbiAgaWYgKG1pblB4ID09PSB1bmRlZmluZWQgJiYgbWF4UHggPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybjtcbiAgfVxuICBydW5XaGVuRm9udFJlYWR5KHZpZXcsICgpID0+IHZpZXcucmVxdWVzdE1lYXN1cmUoe1xuICAgIHJlYWQ6IChtZWFzdXJlVmlldykgPT4ge1xuICAgICAgY29uc3QgbGluZUhlaWdodCA9IHJlYWRMaW5lSGVpZ2h0KG1lYXN1cmVWaWV3KTtcbiAgICAgIGNvbnN0IG9mZnNldFRvcCA9IE1hdGgubWF4KG1lYXN1cmVWaWV3LnNjcm9sbERPTS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBjb250YWluZXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wLCAwKTtcbiAgICAgIGNvbnN0IHRvTGluZXMgPSAocHg6IG51bWJlcikgPT4gTWF0aC5tYXgoMSwgTWF0aC5mbG9vcigocHggLSBvZmZzZXRUb3AgLSA4KSAvIGxpbmVIZWlnaHQpKTtcbiAgICAgIHJldHVybiB7IGxpbmVIZWlnaHQsIG1pbkxpbmVzOiBtaW5QeCA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogdG9MaW5lcyhtaW5QeCksIG1heExpbmVzOiBtYXhQeCA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogdG9MaW5lcyhtYXhQeCkgfTtcbiAgICB9LFxuICAgIHdyaXRlOiAoeyBsaW5lSGVpZ2h0LCBtaW5MaW5lcywgbWF4TGluZXMgfSwgd3JpdGVWaWV3KSA9PiB7XG4gICAgICBpZiAobWF4TGluZXMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICB3cml0ZVZpZXcuc2Nyb2xsRE9NLnN0eWxlLm1heEhlaWdodCA9IGAke21heExpbmVzICogbGluZUhlaWdodCArIDh9cHhgO1xuICAgICAgICB3cml0ZVZpZXcuc2Nyb2xsRE9NLnN0eWxlLm92ZXJmbG93WSA9ICdhdXRvJztcbiAgICAgIH1cbiAgICAgIC8vIG1pbi1oZWlnaHQ6IGLDuSBkw7JuZyB0cuG7kW5nIHbDoG8gY29udGVudCAoaGnhu4duIMSR4bunIGxpbmUtbnVtYmVyIDEuLk4pLCBnaeG7kW5nIGPGoSBjaOG6vyBtaW5MaW5lcy5cbiAgICAgIGlmIChtaW5MaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnN0IGN1cnJlbnRMaW5lcyA9IHdyaXRlVmlldy5zdGF0ZS5kb2MubGluZXM7XG4gICAgICAgIGlmIChjdXJyZW50TGluZXMgPCBtaW5MaW5lcykge1xuICAgICAgICAgIHF1ZXVlTWljcm90YXNrKCgpID0+IHdyaXRlVmlldy5kaXNwYXRjaCh7IGNoYW5nZXM6IHsgZnJvbTogd3JpdGVWaWV3LnN0YXRlLmRvYy5sZW5ndGgsIGluc2VydDogJ1xcbicucmVwZWF0KG1pbkxpbmVzIC0gY3VycmVudExpbmVzKSB9IH0pKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0sXG4gIH0pKTtcbn07XG5cbi8qKlxuICogR2nhu5tpIGjhuqFuIGNoaeG7gXUgY2FvIGVkaXRvciAoeOG7rSBsw70gMSBs4bqnbikuIDIgY2jhur8gxJHhu5kgbG/huqFpIHRy4burIG5oYXU6XG4gKiAtIGBtYXhMaW5lc2A6IGNhcCB0aGVvIHPhu5EgZMOybmcgdHJ1eeG7gW4gdsOgby5cbiAqIC0gYGNvbnRhaW5lcmA6IHThu7EgdMOtbmggc+G7kSBkw7JuZyB0aGVvIGBtaW4taGVpZ2h0YC9gbWF4LWhlaWdodGAgKENTUykgY+G7p2EgY29udGFpbmVyLCBzbmFwIGLhu5lpIHPhu5EgZMOybmcuXG4gKiBD4bqldSBow6xuaCDEkeG7k25nIHRo4budaSBgY29udGFpbmVyYCArIGBtaW5MaW5lc2AvYG1heExpbmVzYCDihpIgdGhyb3cuXG4gKi9cbmV4cG9ydCBjb25zdCBhcHBseU1heEhlaWdodCA9ICh2aWV3OiBFZGl0b3JWaWV3IHwgdW5kZWZpbmVkLCBvcHRpb25zOiB7IG1pbkxpbmVzPzogbnVtYmVyOyBtYXhMaW5lcz86IG51bWJlcjsgY29udGFpbmVyPzogSFRNTEVsZW1lbnQgfSk6IHZvaWQgPT4ge1xuICBjb25zdCB7IG1pbkxpbmVzLCBtYXhMaW5lcywgY29udGFpbmVyIH0gPSBvcHRpb25zO1xuICBpZiAoY29udGFpbmVyICYmIChtaW5MaW5lcyB8fCBtYXhMaW5lcykpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1twcmV2aWV3LXRleHQtZGF0YV0gQ+G6pE0gY+G6pXUgaMOsbmggW2NvbnRhaW5lckVsZW1lbnRdIGPDuW5nIFttaW5MaW5lc10vW21heExpbmVzXS4gQ29udGFpbmVyIHThu7EgcXXhuqNuIGzDvSBtaW4vbWF4IGhlaWdodC4nKTtcbiAgfVxuICBpZiAoIXZpZXcpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKGNvbnRhaW5lcikge1xuICAgIGFwcGx5Q29udGFpbmVySGVpZ2h0KHZpZXcsIGNvbnRhaW5lcik7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmICghbWF4TGluZXMgfHwgbWF4TGluZXMgPD0gMCkge1xuICAgIHJldHVybjtcbiAgfVxuICBydW5XaGVuRm9udFJlYWR5KHZpZXcsICgpID0+IHZpZXcucmVxdWVzdE1lYXN1cmUoe1xuICAgIHJlYWQ6IChtZWFzdXJlVmlldykgPT4gcmVhZExpbmVIZWlnaHQobWVhc3VyZVZpZXcpLFxuICAgIHdyaXRlOiAobGluZUhlaWdodCwgd3JpdGVWaWV3KSA9PiB7XG4gICAgICB3cml0ZVZpZXcuc2Nyb2xsRE9NLnN0eWxlLm1heEhlaWdodCA9IGAke21heExpbmVzICogbGluZUhlaWdodCArIDh9cHhgO1xuICAgICAgd3JpdGVWaWV3LnNjcm9sbERPTS5zdHlsZS5vdmVyZmxvd1kgPSAnYXV0byc7XG4gICAgfSxcbiAgfSkpO1xufTtcbiJdfQ==
|