@richscripts/richtexteditor 2.4.0 → 2.4.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/migration/README.md +100 -0
- package/migration/cli.js +294 -0
- package/migration/index.d.ts +18 -0
- package/migration/index.js +242 -0
- package/migration/report-schema-v2.json +127 -0
- package/package.json +11 -1
- package/richtexteditor/CHANGELOG.md +326 -1
- package/richtexteditor/all_plugins.js +14959 -4832
- package/richtexteditor/all_plugins.min.js +62 -42
- package/richtexteditor/plugins/accessibilitychecker.js +8 -7
- package/richtexteditor/plugins/aitoolkit.css +17 -8
- package/richtexteditor/plugins/aitoolkit.js +8 -8
- package/richtexteditor/plugins/all_plugins.js +14959 -4832
- package/richtexteditor/plugins/all_plugins.min.js +62 -42
- package/richtexteditor/plugins/autocorrect.js +353 -0
- package/richtexteditor/plugins/bookmarkcard.js +12 -1
- package/richtexteditor/plugins/changecase.js +238 -0
- package/richtexteditor/plugins/comments.js +15 -28
- package/richtexteditor/plugins/contentminimap.js +6 -5
- package/richtexteditor/plugins/crossreference.js +408 -0
- package/richtexteditor/plugins/documentimport.js +915 -21
- package/richtexteditor/plugins/documentoutline.js +5 -4
- package/richtexteditor/plugins/docxexport.js +960 -0
- package/richtexteditor/plugins/draghandle.js +309 -0
- package/richtexteditor/plugins/footnotes.js +398 -0
- package/richtexteditor/plugins/formatpainter.js +298 -0
- package/richtexteditor/plugins/formattingmarks.js +246 -0
- package/richtexteditor/plugins/html2pdf.js +108 -4
- package/richtexteditor/plugins/imageeditor.js +37 -7
- package/richtexteditor/plugins/insertgallery.js +553 -170
- package/richtexteditor/plugins/keyboarda11y.js +316 -0
- package/richtexteditor/plugins/linenumbers.js +352 -0
- package/richtexteditor/plugins/linkchecker.js +396 -0
- package/richtexteditor/plugins/mentionplugin.js +4 -3
- package/richtexteditor/plugins/mergefields.js +328 -0
- package/richtexteditor/plugins/multilevellist.js +137 -0
- package/richtexteditor/plugins/pagination.js +505 -0
- package/richtexteditor/plugins/pdfexport.js +1137 -0
- package/richtexteditor/plugins/permanentpen.js +211 -0
- package/richtexteditor/plugins/revisionhistory.js +47 -34
- package/richtexteditor/plugins/rtlui.js +150 -0
- package/richtexteditor/plugins/sanitizer.js +486 -0
- package/richtexteditor/plugins/slashcommand.js +142 -17
- package/richtexteditor/plugins/spellcheck.js +6 -3
- package/richtexteditor/plugins/tableofcontents.js +353 -0
- package/richtexteditor/plugins/tabletools.js +342 -0
- package/richtexteditor/plugins/textdirection.js +313 -0
- package/richtexteditor/plugins/trackedchanges.js +37 -6
- package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +1 -1
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +1 -1
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +1 -1
- package/richtexteditor/plugins/vendor/html2pdf.bundle.min.js +3 -0
- package/richtexteditor/plugins/watermark.js +214 -0
- package/richtexteditor/plugins/wordexport.js +0 -0
- package/richtexteditor/plugins/yjscollab.js +49 -11
- package/richtexteditor/rte-config.js +44 -44
- package/richtexteditor/rte.js +91 -63
- package/richtexteditor/rte_theme_default.css +462 -384
|
@@ -564,60 +564,47 @@ function RTE_Plugin_Comments() {
|
|
|
564
564
|
style.setAttribute("data-rte-comments", "1");
|
|
565
565
|
style.textContent = [
|
|
566
566
|
".rte-comment-sidebar-host{display:flex!important;align-items:stretch}",
|
|
567
|
-
"
|
|
567
|
+
"/* 2026-07-12 comments panel precision */",
|
|
568
|
+
".rte-comment-sidebar{width:310px;min-width:270px;max-width:min(360px,32vw);margin-left:12px;padding:0;background:linear-gradient(180deg,rgba(255,255,255,.98),rgba(248,251,255,.96));border:1px solid rgba(148,163,184,.22);border-radius:8px;font-family:Aptos,'Segoe UI',sans-serif;font-size:13px;color:#172033;display:flex;flex-direction:column;max-height:calc(100vh - 120px);overflow:hidden;box-shadow:0 14px 32px rgba(29,78,216,.1),0 2px 8px rgba(15,23,42,.06)}",
|
|
568
569
|
".rte-comment-sidebar-header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 10px 9px 14px;border-bottom:1px solid rgba(148,163,184,.16);font-weight:750;background:rgba(255,255,255,.72)}",
|
|
569
570
|
".rte-comment-sidebar-title{font-size:13px;font-weight:850;letter-spacing:.02em}",
|
|
570
|
-
".rte-comment-sidebar-close{width:28px;height:28px;border:1px solid rgba(100,116,139,.18);background:#fff;cursor:pointer;color:#64748b;padding:0;border-radius:
|
|
571
|
+
".rte-comment-sidebar-close{width:28px;height:28px;border:1px solid rgba(100,116,139,.18);background:#fff;cursor:pointer;color:#64748b;padding:0;border-radius:7px;display:inline-flex;align-items:center;justify-content:center}",
|
|
571
572
|
".rte-comment-sidebar-close:hover,.rte-comment-sidebar-close:focus-visible{background:#eef4ff;color:#0f3f9f;border-color:rgba(37,99,235,.28)}",
|
|
572
573
|
".rte-comment-list{flex:1;min-height:0;overflow-y:auto;padding:6px;scrollbar-width:thin}",
|
|
573
|
-
".rte-comment-empty{margin:6px;padding:14px;color:#52657e;font-size:12px;font-weight:700;line-height:1.45;text-align:center;background:#fff;border:1px dashed rgba(148,163,184,.28);border-radius:
|
|
574
|
-
".rte-comment-item{padding:10px;border:1px solid rgba(148,163,184,.14);border-radius:
|
|
574
|
+
".rte-comment-empty{margin:6px;padding:14px;color:#52657e;font-size:12px;font-weight:700;line-height:1.45;text-align:center;background:#fff;border:1px dashed rgba(148,163,184,.28);border-radius:8px}",
|
|
575
|
+
".rte-comment-item{padding:10px;border:1px solid rgba(148,163,184,.14);border-radius:8px;background:rgba(255,255,255,.82);box-shadow:0 6px 14px rgba(15,23,42,.04);transition:background 200ms ease,box-shadow 200ms ease,transform 200ms ease}",
|
|
575
576
|
".rte-comment-item+.rte-comment-item{margin-top:7px}",
|
|
576
577
|
".rte-comment-item-active{background:#eef4ff;box-shadow:0 12px 28px rgba(37,99,235,.14);transform:translateY(-1px)}",
|
|
577
578
|
".rte-comment-item-top{display:grid;grid-template-columns:30px minmax(0,1fr) auto;align-items:center;gap:8px}",
|
|
578
|
-
".rte-comment-avatar{width:30px;height:30px;border-radius:
|
|
579
|
+
".rte-comment-avatar{width:30px;height:30px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:11px;font-weight:800;flex:0 0 30px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.28)}",
|
|
579
580
|
".rte-comment-namewrap{flex:1;min-width:0}",
|
|
580
581
|
".rte-comment-name{font-weight:800;font-size:12px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
581
582
|
".rte-comment-when{font-size:11px;color:#64748b}",
|
|
582
583
|
".rte-comment-actions{display:flex;gap:4px;align-items:center}",
|
|
583
|
-
".rte-comment-action{font-size:11px;font-weight:750;padding:4px 8px;border-radius:
|
|
584
|
+
".rte-comment-action{font-size:11px;font-weight:750;padding:4px 8px;border-radius:6px;border:1px solid rgba(100,116,139,.16);background:#fff;cursor:pointer}",
|
|
584
585
|
".rte-comment-action:hover,.rte-comment-action:focus-visible{transform:translateY(-1px)}",
|
|
585
586
|
".rte-comment-resolve{color:#0f3f9f;border-color:rgba(37,99,235,.2);background:#eef4ff}",
|
|
586
587
|
".rte-comment-delete{color:#9f1239;border-color:rgba(159,18,57,.18);background:#fff1f2}",
|
|
587
|
-
".rte-comment-quote{margin:8px 0 0;padding:7px 9px;background:#fff8e8;border:1px solid rgba(245,158,11,.18);border-left:3px solid #f59e0b;border-radius:
|
|
588
|
+
".rte-comment-quote{margin:8px 0 0;padding:7px 9px;background:#fff8e8;border:1px solid rgba(245,158,11,.18);border-left:3px solid #f59e0b;border-radius:7px;font-size:11px;color:#67430a;cursor:pointer;line-height:1.35}",
|
|
588
589
|
".rte-comment-quote:hover{background:#fff3cf}",
|
|
589
590
|
".rte-comment-body{margin-top:8px;font-size:13px;line-height:1.45;color:#172033;white-space:pre-wrap}",
|
|
590
591
|
".rte-comment-reply{display:flex;gap:8px;margin-top:10px;padding-top:9px;border-top:1px dashed rgba(148,163,184,.22)}",
|
|
591
|
-
".rte-comment-reply-avatar{width:24px;height:24px;border-radius:
|
|
592
|
+
".rte-comment-reply-avatar{width:24px;height:24px;border-radius:7px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:10px;font-weight:800;flex:0 0 24px}",
|
|
592
593
|
".rte-comment-reply-body{flex:1;min-width:0}",
|
|
593
594
|
".rte-comment-reply-name{font-size:11px;color:#64748b}",
|
|
594
595
|
".rte-comment-reply-text{font-size:12px;color:#172033;line-height:1.45;white-space:pre-wrap;margin-top:2px}",
|
|
595
596
|
".rte-comment-reply-composer{margin-top:8px;display:flex}",
|
|
596
|
-
".rte-comment-reply-input{flex:1;min-width:0;padding:7px 10px;border-radius:
|
|
597
|
+
".rte-comment-reply-input{flex:1;min-width:0;padding:7px 10px;border-radius:7px;border:1px solid rgba(148,163,184,.24);font:inherit;font-size:12px;background:#fff}",
|
|
597
598
|
".rte-comment-reply-input:focus,.rte-comment-composer-textarea:focus{outline:2px solid rgba(37,99,235,.18);border-color:rgba(37,99,235,.38)}",
|
|
598
|
-
".rte-comment-composer{position:absolute;z-index:2147483000;width:min(320px,calc(100vw - 24px));background:linear-gradient(180deg,#fff,#f8fbff);border:1px solid rgba(148,163,184,.22);box-shadow:0
|
|
599
|
+
".rte-comment-composer{position:absolute;z-index:2147483000;width:min(320px,calc(100vw - 24px));background:linear-gradient(180deg,#fff,#f8fbff);border:1px solid rgba(148,163,184,.22);box-shadow:0 14px 32px rgba(29,78,216,.12),0 2px 8px rgba(15,23,42,.08);border-radius:8px;padding:12px;font-family:Aptos,'Segoe UI',sans-serif;color:#172033}",
|
|
599
600
|
".rte-comment-composer-header{font-weight:850;font-size:13px;margin-bottom:8px;color:#172033}",
|
|
600
|
-
".rte-comment-composer-textarea{width:100%;box-sizing:border-box;padding:9px 10px;border-radius:
|
|
601
|
+
".rte-comment-composer-textarea{width:100%;box-sizing:border-box;padding:9px 10px;border-radius:7px;border:1px solid rgba(148,163,184,.24);font:inherit;font-size:13px;resize:vertical;min-height:70px;background:#fff}",
|
|
601
602
|
".rte-comment-composer-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:9px}",
|
|
602
|
-
".rte-comment-btn{min-height:30px;padding:0 13px;border-radius:
|
|
603
|
+
".rte-comment-btn{min-height:30px;padding:0 13px;border-radius:7px;border:1px solid rgba(100,116,139,.18);font:750 12px/1 Aptos,'Segoe UI',sans-serif;cursor:pointer}",
|
|
603
604
|
".rte-comment-btn-ghost{background:#fff;color:#52657e}",
|
|
604
605
|
".rte-comment-btn-primary{background:#1d67ba;color:#fff;border-color:#1d67ba;box-shadow:0 8px 16px rgba(29,103,186,.18)}",
|
|
605
|
-
//
|
|
606
|
-
//
|
|
607
|
-
// automatically via prefers-color-scheme, and via an explicit
|
|
608
|
-
// .rte-comment-dark class set when the editor is forced dark.
|
|
609
|
-
"@media (prefers-color-scheme:dark){",
|
|
610
|
-
" .rte-comment-sidebar,.rte-comment-composer{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155;color:#e2e8f0}",
|
|
611
|
-
" .rte-comment-sidebar-header{background:rgba(30,41,59,.72);border-color:#334155}",
|
|
612
|
-
" .rte-comment-item{background:rgba(30,41,59,.85);border-color:#334155}",
|
|
613
|
-
" .rte-comment-item-active{background:#1e3a8a}",
|
|
614
|
-
" .rte-comment-empty{background:#0f172a;color:#94a3b8;border-color:#334155}",
|
|
615
|
-
" .rte-comment-when,.rte-comment-reply-name{color:#94a3b8}",
|
|
616
|
-
" .rte-comment-body,.rte-comment-reply-text,.rte-comment-composer-header{color:#e2e8f0}",
|
|
617
|
-
" .rte-comment-reply-input,.rte-comment-composer-textarea{background:#0f172a;color:#e2e8f0;border-color:#334155}",
|
|
618
|
-
" .rte-comment-action,.rte-comment-btn-ghost,.rte-comment-sidebar-close{background:#0f172a;color:#cbd5e1;border-color:#334155}",
|
|
619
|
-
" .rte-comment-quote{background:rgba(245,158,11,.12);color:#fcd34d}",
|
|
620
|
-
"}",
|
|
606
|
+
// Body-appended overlays remain light unless the host editor
|
|
607
|
+
// explicitly opts into its rte-dark treatment below.
|
|
621
608
|
".rte-comment-dark.rte-comment-sidebar,.rte-comment-dark.rte-comment-composer{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155;color:#e2e8f0}",
|
|
622
609
|
".rte-comment-dark .rte-comment-sidebar-header{background:rgba(30,41,59,.72);border-color:#334155}",
|
|
623
610
|
".rte-comment-dark .rte-comment-item{background:rgba(30,41,59,.85);border-color:#334155}",
|
|
@@ -81,7 +81,8 @@ function RTE_Plugin_ContentMinimap() {
|
|
|
81
81
|
style.innerHTML = [
|
|
82
82
|
".rte-content-minimap-shell{display:flex;align-items:stretch;gap:10px;}",
|
|
83
83
|
".rte-content-minimap-shell>.rte-content-minimap-host{flex:1 1 auto;min-width:0;}",
|
|
84
|
-
"
|
|
84
|
+
"/* 2026-07-12 navigation panel precision */",
|
|
85
|
+
".rte-content-minimap-panel{display:none;flex:0 0 var(--rte-content-minimap-width,168px);min-width:148px;max-width:220px;border:1px solid rgba(148,163,184,.22);border-radius:8px;background:linear-gradient(180deg,rgba(255,255,255,.98),rgba(248,251,255,.96));box-shadow:0 14px 32px rgba(29,78,216,.1),0 2px 8px rgba(15,23,42,.06);overflow:hidden;color:#172033;font-family:Aptos,'Segoe UI',sans-serif;}",
|
|
85
86
|
".rte-content-minimap-shell.is-open>.rte-content-minimap-panel{display:flex;flex-direction:column;}",
|
|
86
87
|
".rte-content-minimap-header{padding:12px 12px 9px 14px;border-bottom:1px solid rgba(148,163,184,.16);background:rgba(255,255,255,.72);}",
|
|
87
88
|
".rte-content-minimap-kicker{font-size:10px;line-height:1.3;letter-spacing:.08em;text-transform:uppercase;color:#52657e;font-weight:850;}",
|
|
@@ -89,17 +90,17 @@ function RTE_Plugin_ContentMinimap() {
|
|
|
89
90
|
".rte-content-minimap-copy{margin-top:5px;font-size:12px;line-height:1.42;color:#52657e;}",
|
|
90
91
|
".rte-content-minimap-toolbar{display:flex;align-items:center;justify-content:space-between;padding:8px 10px 8px 14px;border-bottom:1px solid rgba(148,163,184,.16);gap:8px;background:rgba(255,255,255,.52);}",
|
|
91
92
|
".rte-content-minimap-meta{min-width:0;font-size:12px;font-weight:750;color:#52657e;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}",
|
|
92
|
-
".rte-content-minimap-close{appearance:none;border:1px solid rgba(100,116,139,.18);background:#fff;color:#315277;cursor:pointer;font-size:12px;font-weight:750;padding:6px 9px;border-radius:
|
|
93
|
+
".rte-content-minimap-close{appearance:none;border:1px solid rgba(100,116,139,.18);background:#fff;color:#315277;cursor:pointer;font-size:12px;font-weight:750;padding:6px 9px;border-radius:7px;line-height:1;}",
|
|
93
94
|
".rte-content-minimap-close:hover,.rte-content-minimap-close:focus-visible{background:#eef4ff;color:#0f3f9f;border-color:rgba(37,99,235,.28);}",
|
|
94
95
|
".rte-content-minimap-body{padding:6px;overflow:auto;min-height:140px;max-height:520px;scrollbar-width:thin;}",
|
|
95
|
-
".rte-content-minimap-canvas{position:relative;border-radius:
|
|
96
|
+
".rte-content-minimap-canvas{position:relative;border-radius:8px;background:#fff;border:1px solid rgba(148,163,184,.18);overflow:hidden;min-height:120px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.7);}",
|
|
96
97
|
".rte-content-minimap-preview{position:relative;transform-origin:top left;pointer-events:none;color:#172033;background:#fff;}",
|
|
97
98
|
".rte-content-minimap-preview>*{max-width:100%;}",
|
|
98
99
|
".rte-content-minimap-preview img,.rte-content-minimap-preview table,.rte-content-minimap-preview iframe,.rte-content-minimap-preview video{max-width:100% !important;}",
|
|
99
100
|
".rte-content-minimap-preview [contenteditable]{pointer-events:none;}",
|
|
100
|
-
".rte-content-minimap-viewport{position:absolute;left:0;right:0;border:1px solid rgba(37,99,235,.62);background:linear-gradient(180deg,rgba(37,99,235,.18) 0%,rgba(14,165,233,.12) 100%);box-shadow:inset 0 0 0 1px rgba(255,255,255,.48),0
|
|
101
|
+
".rte-content-minimap-viewport{position:absolute;left:0;right:0;border:1px solid rgba(37,99,235,.62);background:linear-gradient(180deg,rgba(37,99,235,.18) 0%,rgba(14,165,233,.12) 100%);box-shadow:inset 0 0 0 1px rgba(255,255,255,.48),0 6px 14px rgba(37,99,235,.14);border-radius:7px;cursor:grab;}",
|
|
101
102
|
".rte-content-minimap-viewport.is-dragging{cursor:grabbing;}",
|
|
102
|
-
".rte-content-minimap-empty{position:absolute;inset:auto 8px 8px 8px;padding:10px;border-radius:
|
|
103
|
+
".rte-content-minimap-empty{position:absolute;inset:auto 8px 8px 8px;padding:10px;border-radius:7px;background:rgba(255,255,255,.94);color:#52657e;font-size:12px;font-weight:700;line-height:1.45;border:1px dashed rgba(148,163,184,.32);}",
|
|
103
104
|
"@media (max-width: 1100px){.rte-content-minimap-shell{display:block;}.rte-content-minimap-panel{margin-top:12px;max-width:none;width:100%;}.rte-content-minimap-body{max-height:280px;}}"
|
|
104
105
|
].join("");
|
|
105
106
|
hostDoc.head.appendChild(style);
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-07-27 Cross-references. "See clause 3.2", "Figure 4", "as described in
|
|
4
|
+
// Scope of Work (page 7)" — references that keep pointing at the right thing
|
|
5
|
+
// after the document is edited around them.
|
|
6
|
+
//
|
|
7
|
+
// This is the piece that makes the rest of the document suite hold together.
|
|
8
|
+
// Legal numbering renumbers clauses, the table of contents re-derives itself,
|
|
9
|
+
// footnotes renumber — and until now a sentence saying "see clause 3.2" stayed
|
|
10
|
+
// frozen at 3.2 while the clause it meant became 4.1. A stale cross-reference in
|
|
11
|
+
// a contract is not cosmetic; it changes what the contract says.
|
|
12
|
+
//
|
|
13
|
+
// Competitive position (checked 2026-07-27): CKEditor 5 ships Bookmarks
|
|
14
|
+
// (in-text anchors) and its own auto-updating table of contents, but describes
|
|
15
|
+
// auto-updating cross-references as a PLANNED enhancement rather than a current
|
|
16
|
+
// feature. TinyMCE documents no cross-reference plugin. Word has had this for
|
|
17
|
+
// decades and it is table stakes for contract and specification work.
|
|
18
|
+
//
|
|
19
|
+
// Design notes:
|
|
20
|
+
// - A reference is an atomic contenteditable=false chip carrying the target id
|
|
21
|
+
// and the display format. The visible text is REAL TEXT, recomputed on every
|
|
22
|
+
// mutation, so the saved HTML stands alone outside the editor (same reasoning
|
|
23
|
+
// as footnotes.js).
|
|
24
|
+
// - A reference whose target has been deleted renders a visible error rather
|
|
25
|
+
// than silently keeping its last value. Word does this too, and it is the
|
|
26
|
+
// right call: a wrong-but-plausible clause number is far more dangerous than
|
|
27
|
+
// an obviously broken one.
|
|
28
|
+
RTE_DefaultConfig.plugin_crossreference = RTE_Plugin_CrossReference;
|
|
29
|
+
|
|
30
|
+
// Shown when the target no longer exists.
|
|
31
|
+
if (typeof RTE_DefaultConfig.crossRefMissingText === "undefined") RTE_DefaultConfig.crossRefMissingText = "[reference not found]";
|
|
32
|
+
// Prefixes used when auto-numbering tables and figures.
|
|
33
|
+
if (typeof RTE_DefaultConfig.crossRefTableLabel === "undefined") RTE_DefaultConfig.crossRefTableLabel = "Table";
|
|
34
|
+
if (typeof RTE_DefaultConfig.crossRefFigureLabel === "undefined") RTE_DefaultConfig.crossRefFigureLabel = "Figure";
|
|
35
|
+
|
|
36
|
+
function RTE_Plugin_CrossReference() {
|
|
37
|
+
var obj = this;
|
|
38
|
+
var config, editor;
|
|
39
|
+
var boundDoc = null;
|
|
40
|
+
var queued = false;
|
|
41
|
+
var idSeq = 0;
|
|
42
|
+
|
|
43
|
+
obj.PluginName = "CrossReference";
|
|
44
|
+
|
|
45
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
46
|
+
|
|
47
|
+
obj.InitEditor = function (argeditor) {
|
|
48
|
+
editor = argeditor;
|
|
49
|
+
|
|
50
|
+
editor.attachEvent("exec_command_insertcrossreference", function (state) {
|
|
51
|
+
state.returnValue = true;
|
|
52
|
+
var v = state && state.value;
|
|
53
|
+
if (v && typeof v === "object") obj.Insert(v.target, v.format);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
setup();
|
|
57
|
+
try { editor.attachEvent("ready", setup); } catch (e) {}
|
|
58
|
+
try { editor.attachEvent("aftersethtml", function () { setup(); queue(); }); } catch (e) {}
|
|
59
|
+
setTimeout(function () { setup(); queue(); }, 0);
|
|
60
|
+
|
|
61
|
+
// Public API.
|
|
62
|
+
editor.listCrossReferenceTargets = function () { return obj.Targets(); };
|
|
63
|
+
editor.insertCrossReference = function (targetId, format) { return obj.Insert(targetId, format); };
|
|
64
|
+
editor.updateCrossReferences = function () { return refresh(); };
|
|
65
|
+
editor.getCrossReferences = function () { return obj.List(); };
|
|
66
|
+
editor.getCrossReferenceCss = function () { return css(); };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
function setup() {
|
|
70
|
+
var doc = getDoc();
|
|
71
|
+
if (!doc) return;
|
|
72
|
+
injectStyles(doc);
|
|
73
|
+
if (doc === boundDoc) return;
|
|
74
|
+
boundDoc = doc;
|
|
75
|
+
var editable = getEditable();
|
|
76
|
+
if (!editable) return;
|
|
77
|
+
editable.addEventListener("keyup", queue);
|
|
78
|
+
editable.addEventListener("cut", queue);
|
|
79
|
+
editable.addEventListener("paste", queue);
|
|
80
|
+
editable.addEventListener("drop", queue);
|
|
81
|
+
editable.addEventListener("click", function (e) {
|
|
82
|
+
var chip = closestClass(e.target, "rte-xref", editable);
|
|
83
|
+
if (!chip) return;
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
var t = resolveTarget(chip.getAttribute("data-xref-target"));
|
|
86
|
+
if (!t) return;
|
|
87
|
+
try { t.el.scrollIntoView({ block: "center" }); } catch (e2) { t.el.scrollIntoView(); }
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getDoc() { try { return editor.getDocument(); } catch (e) { return null; } }
|
|
92
|
+
function getEditable() { try { return editor.getEditable(); } catch (e) { return null; } }
|
|
93
|
+
|
|
94
|
+
function queue() {
|
|
95
|
+
if (queued) return;
|
|
96
|
+
queued = true;
|
|
97
|
+
setTimeout(function () { queued = false; refresh(); }, 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function closestClass(node, cls, root) {
|
|
101
|
+
while (node && node !== root) {
|
|
102
|
+
if (node.nodeType === 1 && node.classList && node.classList.contains(cls)) return node;
|
|
103
|
+
node = node.parentNode;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function newId(prefix) {
|
|
109
|
+
return prefix + Math.floor(Math.random() * 1e9).toString(36) + (idSeq++).toString(36);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function ensureId(el, prefix) {
|
|
113
|
+
var id = el.getAttribute("id");
|
|
114
|
+
if (id) return id;
|
|
115
|
+
id = newId(prefix);
|
|
116
|
+
el.setAttribute("id", id);
|
|
117
|
+
return id;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---- targets ---------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
// Everything in the document that can be referred to, in document order.
|
|
123
|
+
obj.Targets = function () {
|
|
124
|
+
var editable = getEditable();
|
|
125
|
+
if (!editable) return [];
|
|
126
|
+
var out = [];
|
|
127
|
+
|
|
128
|
+
var headings = editable.querySelectorAll("h1,h2,h3,h4,h5,h6");
|
|
129
|
+
for (var i = 0; i < headings.length; i++) {
|
|
130
|
+
var h = headings[i];
|
|
131
|
+
// Generated regions are not referable content.
|
|
132
|
+
if (h.closest && (h.closest("nav.rte-toc") || h.closest("section.rte-footnotes"))) continue;
|
|
133
|
+
var text = (h.textContent || "").trim();
|
|
134
|
+
if (!text) continue;
|
|
135
|
+
out.push({ id: ensureId(h, "rte-h-"), type: "heading", label: text, number: null });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Legal-numbered clauses: the number is derived from position, exactly
|
|
139
|
+
// like the CSS counters that render it.
|
|
140
|
+
var roots = editable.querySelectorAll("ol.rte-legal-list");
|
|
141
|
+
for (var r = 0; r < roots.length; r++) collectClauses(roots[r], [], out);
|
|
142
|
+
|
|
143
|
+
var notes = editable.querySelectorAll("li.rte-fn-note[data-fn-id]");
|
|
144
|
+
for (var n = 0; n < notes.length; n++) {
|
|
145
|
+
out.push({
|
|
146
|
+
id: ensureId(notes[n], "rte-fn-"),
|
|
147
|
+
type: "footnote",
|
|
148
|
+
label: (noteText(notes[n]) || "footnote"),
|
|
149
|
+
number: notes[n].getAttribute("data-fn-number")
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var tables = editable.querySelectorAll("table");
|
|
154
|
+
var tnum = 0;
|
|
155
|
+
for (var t = 0; t < tables.length; t++) {
|
|
156
|
+
tnum++;
|
|
157
|
+
var cap = tables[t].querySelector("caption");
|
|
158
|
+
out.push({
|
|
159
|
+
id: ensureId(tables[t], "rte-tbl-"),
|
|
160
|
+
type: "table",
|
|
161
|
+
label: (cap && cap.textContent.trim()) || (String(config.crossRefTableLabel) + " " + tnum),
|
|
162
|
+
number: String(tnum)
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
var figs = editable.querySelectorAll("figure, img");
|
|
167
|
+
var fnum = 0;
|
|
168
|
+
for (var f = 0; f < figs.length; f++) {
|
|
169
|
+
// An <img> inside a <figure> is the same figure, counted once.
|
|
170
|
+
if (figs[f].nodeName === "IMG" && figs[f].closest && figs[f].closest("figure")) continue;
|
|
171
|
+
fnum++;
|
|
172
|
+
var fc = figs[f].querySelector ? figs[f].querySelector("figcaption") : null;
|
|
173
|
+
out.push({
|
|
174
|
+
id: ensureId(figs[f], "rte-fig-"),
|
|
175
|
+
type: "figure",
|
|
176
|
+
label: (fc && fc.textContent.trim()) || (String(config.crossRefFigureLabel) + " " + fnum),
|
|
177
|
+
number: String(fnum)
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
function collectClauses(ol, path, out) {
|
|
184
|
+
var idx = 0;
|
|
185
|
+
for (var i = 0; i < ol.children.length; i++) {
|
|
186
|
+
var li = ol.children[i];
|
|
187
|
+
if (li.nodeName !== "LI") continue;
|
|
188
|
+
idx++;
|
|
189
|
+
var here = path.concat([idx]);
|
|
190
|
+
var first = firstLineText(li);
|
|
191
|
+
out.push({
|
|
192
|
+
id: ensureId(li, "rte-cl-"),
|
|
193
|
+
type: "clause",
|
|
194
|
+
label: first || ("clause " + here.join(".")),
|
|
195
|
+
number: here.join(".")
|
|
196
|
+
});
|
|
197
|
+
for (var c = 0; c < li.children.length; c++) {
|
|
198
|
+
if (li.children[c].nodeName === "OL") collectClauses(li.children[c], here, out);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// The clause's own text, excluding any nested sub-list.
|
|
204
|
+
function firstLineText(li) {
|
|
205
|
+
var s = "";
|
|
206
|
+
for (var i = 0; i < li.childNodes.length; i++) {
|
|
207
|
+
var n = li.childNodes[i];
|
|
208
|
+
if (n.nodeType === 1 && (n.nodeName === "OL" || n.nodeName === "UL")) break;
|
|
209
|
+
s += n.textContent || "";
|
|
210
|
+
}
|
|
211
|
+
return s.trim().replace(/\s+/g, " ").slice(0, 80);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function noteText(note) {
|
|
215
|
+
var clone = note.cloneNode(true);
|
|
216
|
+
var backs = clone.querySelectorAll ? clone.querySelectorAll("a.rte-fn-back") : [];
|
|
217
|
+
for (var i = 0; i < backs.length; i++) if (backs[i].parentNode) backs[i].parentNode.removeChild(backs[i]);
|
|
218
|
+
return (clone.textContent || "").replace(/↩/g, "").trim().slice(0, 80);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function resolveTarget(id) {
|
|
222
|
+
if (!id) return null;
|
|
223
|
+
var list = obj.Targets();
|
|
224
|
+
for (var i = 0; i < list.length; i++) {
|
|
225
|
+
if (list[i].id !== id) continue;
|
|
226
|
+
var editable = getEditable();
|
|
227
|
+
var el = editable ? editable.querySelector("#" + id.replace(/["\\\]\[]/g, "\\$&")) : null;
|
|
228
|
+
if (!el) return null;
|
|
229
|
+
var t = list[i];
|
|
230
|
+
t.el = el;
|
|
231
|
+
return t;
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ---- insert ----------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
// format: "text" | "number" | "page" | "position" | "label"
|
|
239
|
+
obj.Insert = function (targetId, format) {
|
|
240
|
+
var doc = getDoc();
|
|
241
|
+
var editable = getEditable();
|
|
242
|
+
if (!doc || !editable) return false;
|
|
243
|
+
var target = resolveTarget(targetId);
|
|
244
|
+
if (!target) return false;
|
|
245
|
+
format = String(format || "text");
|
|
246
|
+
|
|
247
|
+
var chip = doc.createElement("span");
|
|
248
|
+
chip.className = "rte-xref";
|
|
249
|
+
chip.setAttribute("data-xref-target", targetId);
|
|
250
|
+
chip.setAttribute("data-xref-format", format);
|
|
251
|
+
chip.setAttribute("contenteditable", "false");
|
|
252
|
+
chip.textContent = displayFor(target, format, chip);
|
|
253
|
+
|
|
254
|
+
var placed = false;
|
|
255
|
+
try {
|
|
256
|
+
if (typeof editor.insertElement === "function") { editor.insertElement(chip); placed = true; }
|
|
257
|
+
} catch (e) {}
|
|
258
|
+
if (!placed) {
|
|
259
|
+
try {
|
|
260
|
+
var sel = editor.getSelection();
|
|
261
|
+
if (sel && sel.rangeCount) {
|
|
262
|
+
var r = sel.getRangeAt(0);
|
|
263
|
+
r.collapse(false);
|
|
264
|
+
r.insertNode(chip);
|
|
265
|
+
r.setStartAfter(chip); r.collapse(true);
|
|
266
|
+
sel.removeAllRanges(); sel.addRange(r);
|
|
267
|
+
placed = true;
|
|
268
|
+
}
|
|
269
|
+
} catch (e) {}
|
|
270
|
+
}
|
|
271
|
+
if (!placed) editable.appendChild(chip);
|
|
272
|
+
// Recompute now that the chip is actually IN the document. The text set
|
|
273
|
+
// above was computed while it was still detached, and the "position"
|
|
274
|
+
// format compares the chip against its target — on a detached node that
|
|
275
|
+
// comparison is meaningless, so a reference to something earlier in the
|
|
276
|
+
// document displayed "below" until the next refresh happened to fix it.
|
|
277
|
+
refresh();
|
|
278
|
+
fireChange();
|
|
279
|
+
return targetId;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
function displayFor(target, format, chip) {
|
|
283
|
+
if (!target) return String(config.crossRefMissingText);
|
|
284
|
+
switch (format) {
|
|
285
|
+
case "number":
|
|
286
|
+
if (target.number) return target.number;
|
|
287
|
+
return target.label; // headings have no number
|
|
288
|
+
case "label":
|
|
289
|
+
// "Table 2" / "Figure 3" / "clause 3.2" style
|
|
290
|
+
if (target.type === "table") return String(config.crossRefTableLabel) + " " + target.number;
|
|
291
|
+
if (target.type === "figure") return String(config.crossRefFigureLabel) + " " + target.number;
|
|
292
|
+
if (target.type === "clause") return "clause " + target.number;
|
|
293
|
+
if (target.type === "footnote") return "footnote " + target.number;
|
|
294
|
+
return target.label;
|
|
295
|
+
case "page":
|
|
296
|
+
var p = null;
|
|
297
|
+
try {
|
|
298
|
+
if (typeof editor.getPageOfElement === "function") p = editor.getPageOfElement(target.el);
|
|
299
|
+
} catch (e) { p = null; }
|
|
300
|
+
// No page view means no honest page number; keep whatever was
|
|
301
|
+
// last computed rather than inventing or blanking one.
|
|
302
|
+
if (p) return String(p);
|
|
303
|
+
var prev = chip ? chip.getAttribute("data-xref-last-page") : null;
|
|
304
|
+
return prev || "?";
|
|
305
|
+
case "position":
|
|
306
|
+
return positionOf(target.el, chip);
|
|
307
|
+
default:
|
|
308
|
+
return target.label;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// "above" / "below" relative to the reference itself.
|
|
313
|
+
function positionOf(targetEl, chip) {
|
|
314
|
+
if (!targetEl || !chip) return "above";
|
|
315
|
+
try {
|
|
316
|
+
var pos = chip.compareDocumentPosition(targetEl);
|
|
317
|
+
if (pos & Node.DOCUMENT_POSITION_PRECEDING) return "above";
|
|
318
|
+
if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return "below";
|
|
319
|
+
} catch (e) {}
|
|
320
|
+
return "above";
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ---- refresh ---------------------------------------------------------
|
|
324
|
+
|
|
325
|
+
function chips() {
|
|
326
|
+
var editable = getEditable();
|
|
327
|
+
if (!editable) return [];
|
|
328
|
+
return Array.prototype.slice.call(editable.querySelectorAll("span.rte-xref[data-xref-target]"));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function refresh() {
|
|
332
|
+
var list = chips();
|
|
333
|
+
if (!list.length) return [];
|
|
334
|
+
// Build the target index once rather than per chip.
|
|
335
|
+
var targets = obj.Targets();
|
|
336
|
+
var byId = {};
|
|
337
|
+
for (var t = 0; t < targets.length; t++) byId[targets[t].id] = targets[t];
|
|
338
|
+
var editable = getEditable();
|
|
339
|
+
|
|
340
|
+
var out = [];
|
|
341
|
+
for (var i = 0; i < list.length; i++) {
|
|
342
|
+
var chip = list[i];
|
|
343
|
+
var id = chip.getAttribute("data-xref-target");
|
|
344
|
+
var fmt = chip.getAttribute("data-xref-format") || "text";
|
|
345
|
+
var target = byId[id] || null;
|
|
346
|
+
if (target) {
|
|
347
|
+
target.el = editable.querySelector("#" + id.replace(/["\\\]\[]/g, "\\$&"));
|
|
348
|
+
}
|
|
349
|
+
var text = displayFor(target, fmt, chip);
|
|
350
|
+
if (chip.textContent !== text) chip.textContent = text;
|
|
351
|
+
if (target) {
|
|
352
|
+
chip.classList.remove("rte-xref-broken");
|
|
353
|
+
if (fmt === "page" && /^\d+$/.test(text)) chip.setAttribute("data-xref-last-page", text);
|
|
354
|
+
} else {
|
|
355
|
+
// Visible failure beats a plausible wrong number.
|
|
356
|
+
chip.classList.add("rte-xref-broken");
|
|
357
|
+
}
|
|
358
|
+
out.push({ target: id, format: fmt, text: text, resolved: !!target });
|
|
359
|
+
}
|
|
360
|
+
return out;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
obj.List = function () {
|
|
364
|
+
var list = chips();
|
|
365
|
+
var out = [];
|
|
366
|
+
for (var i = 0; i < list.length; i++) {
|
|
367
|
+
out.push({
|
|
368
|
+
target: list[i].getAttribute("data-xref-target"),
|
|
369
|
+
format: list[i].getAttribute("data-xref-format") || "text",
|
|
370
|
+
text: list[i].textContent,
|
|
371
|
+
resolved: !list[i].classList.contains("rte-xref-broken")
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
return out;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
function fireChange() {
|
|
378
|
+
try { if (typeof editor.updateDesign === "function") editor.updateDesign(); } catch (e) {}
|
|
379
|
+
try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) {}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ---- styles ----------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
function css() {
|
|
385
|
+
return (
|
|
386
|
+
"span.rte-xref{color:#1474ea;cursor:pointer;border-bottom:1px dotted #9dc2f5;" +
|
|
387
|
+
"user-select:none;}" +
|
|
388
|
+
"span.rte-xref:hover{border-bottom-style:solid;}" +
|
|
389
|
+
"span.rte-xref.rte-xref-broken{color:#c81e1e;background:rgba(200,30,30,.09);" +
|
|
390
|
+
"border-bottom:1px solid #e59b9b;border-radius:2px;padding:0 .15em;}"
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function injectStyles(doc) {
|
|
395
|
+
if (!doc) return;
|
|
396
|
+
var existing = doc.getElementById("rte-xref-styles");
|
|
397
|
+
var text = css();
|
|
398
|
+
if (existing) {
|
|
399
|
+
if (existing.getAttribute("data-css") === text) return;
|
|
400
|
+
existing.parentNode && existing.parentNode.removeChild(existing);
|
|
401
|
+
}
|
|
402
|
+
var st = doc.createElement("style");
|
|
403
|
+
st.id = "rte-xref-styles";
|
|
404
|
+
st.setAttribute("data-css", text);
|
|
405
|
+
st.appendChild(doc.createTextNode(text));
|
|
406
|
+
(doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement).appendChild(st);
|
|
407
|
+
}
|
|
408
|
+
}
|