@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.
@@ -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==