tadforce 0.0.0-stage → 0.3.0
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/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +266 -2
- package/assets/logo-mark.svg +24 -0
- package/assets/logo.svg +31 -0
- package/bin/tadforce.js +4 -0
- package/commands/tadforce-review.md +8 -0
- package/commands/tadforce-update.md +6 -0
- package/commands/tadforce.md +10 -0
- package/package.json +60 -4
- package/skills/tadforce/SKILL.md +73 -0
- package/skills/tadforce/references/axes-rules.md +29 -0
- package/skills/tadforce/references/evidence-rules.md +40 -0
- package/skills/tadforce/references/exclusion-heuristics.md +40 -0
- package/skills/tadforce/references/figure-rules.md +42 -0
- package/skills/tadforce/references/markdown-conventions.md +67 -0
- package/skills/tadforce/references/modules/agentforce-ai.md +24 -0
- package/skills/tadforce/references/modules/analytics.md +19 -0
- package/skills/tadforce/references/modules/commerce.md +25 -0
- package/skills/tadforce/references/modules/data-cloud.md +23 -0
- package/skills/tadforce/references/modules/experience-cloud.md +24 -0
- package/skills/tadforce/references/modules/field-service.md +25 -0
- package/skills/tadforce/references/modules/industries.md +21 -0
- package/skills/tadforce/references/modules/integration-platforms.md +19 -0
- package/skills/tadforce/references/modules/marketing.md +24 -0
- package/skills/tadforce/references/modules/omnistudio.md +24 -0
- package/skills/tadforce/references/modules/platform-events.md +19 -0
- package/skills/tadforce/references/modules/revenue-cpq.md +25 -0
- package/skills/tadforce/references/modules/sales-cloud.md +26 -0
- package/skills/tadforce/references/modules/security-shield.md +18 -0
- package/skills/tadforce/references/modules/service-cloud.md +27 -0
- package/skills/tadforce/references/naming-external-systems.md +32 -0
- package/skills/tadforce/references/questions.md +59 -0
- package/skills/tadforce/references/salesforce-skills.md +27 -0
- package/skills/tadforce/references/workflow.md +95 -0
- package/skills/tadforce/references/writing-rules.md +47 -0
- package/skills/tadforce-review/SKILL.md +53 -0
- package/skills/tadforce-update/SKILL.md +48 -0
- package/src/build/brand.js +107 -0
- package/src/build/browser.js +70 -0
- package/src/build/directives.js +293 -0
- package/src/build/export.js +60 -0
- package/src/build/figures.js +119 -0
- package/src/build/mermaid.js +107 -0
- package/src/build/render.js +299 -0
- package/src/build/serve.js +59 -0
- package/src/cli.js +68 -0
- package/src/commands/docs.js +204 -0
- package/src/commands/doctor.js +370 -0
- package/src/commands/init.js +132 -0
- package/src/commands/org.js +151 -0
- package/src/commands/review.js +35 -0
- package/src/commands/spec.js +91 -0
- package/src/commands/update.js +154 -0
- package/src/facts/extractors/apex.js +111 -0
- package/src/facts/extractors/automation.js +85 -0
- package/src/facts/extractors/data.js +94 -0
- package/src/facts/extractors/integration.js +76 -0
- package/src/facts/extractors/misc.js +100 -0
- package/src/facts/extractors/security.js +92 -0
- package/src/facts/extractors/ui.js +120 -0
- package/src/facts/fingerprints.js +32 -0
- package/src/facts/heuristics/exclusions.js +143 -0
- package/src/facts/heuristics/modules.js +85 -0
- package/src/facts/heuristics/naming.js +31 -0
- package/src/facts/heuristics/signals.js +76 -0
- package/src/facts/index.js +197 -0
- package/src/facts/org.js +87 -0
- package/src/facts/xml.js +85 -0
- package/src/sf/sf.js +120 -0
- package/src/util/args.js +21 -0
- package/src/util/config.js +104 -0
- package/src/util/fsx.js +58 -0
- package/src/util/i18n.js +49 -0
- package/src/util/log.js +31 -0
- package/src/util/paths.js +11 -0
- package/src/validate/index.js +126 -0
- package/templates/axes/en/00-document-control.md +3 -0
- package/templates/axes/en/01-summary.md +7 -0
- package/templates/axes/en/02-method.md +9 -0
- package/templates/axes/en/03-findings.md +19 -0
- package/templates/axes/en/04-register.md +3 -0
- package/templates/axes/en/05-roadmap.md +3 -0
- package/templates/axes/en/99-appendix.md +5 -0
- package/templates/axes/fr/00-document-control.md +3 -0
- package/templates/axes/fr/01-summary.md +7 -0
- package/templates/axes/fr/02-method.md +9 -0
- package/templates/axes/fr/03-findings.md +19 -0
- package/templates/axes/fr/04-register.md +3 -0
- package/templates/axes/fr/05-roadmap.md +3 -0
- package/templates/axes/fr/99-appendix.md +5 -0
- package/templates/html/cover.html +12 -0
- package/templates/html/editor.js +502 -0
- package/templates/html/style.css +255 -0
- package/templates/html/template.html +88 -0
- package/templates/i18n/en.json +366 -0
- package/templates/i18n/fr.json +366 -0
- package/templates/manifest/package-tad.xml +543 -0
- package/templates/specs/sections.yml +494 -0
- package/templates/tad/en/00-document-control.md +7 -0
- package/templates/tad/en/01-summary.md +17 -0
- package/templates/tad/en/02-purpose.md +35 -0
- package/templates/tad/en/03-context.md +17 -0
- package/templates/tad/en/04-general.md +29 -0
- package/templates/tad/en/05-functional.md +7 -0
- package/templates/tad/en/06-data.md +71 -0
- package/templates/tad/en/07-application.md +49 -0
- package/templates/tad/en/08-ui.md +41 -0
- package/templates/tad/en/09-integrations.md +53 -0
- package/templates/tad/en/10-security.md +59 -0
- package/templates/tad/en/11-reporting.md +13 -0
- package/templates/tad/en/12-operations.md +25 -0
- package/templates/tad/en/13-nfr.md +9 -0
- package/templates/tad/en/14-decisions.md +3 -0
- package/templates/tad/en/15-outlook.md +3 -0
- package/templates/tad/en/99-appendices.md +119 -0
- package/templates/tad/fr/00-document-control.md +7 -0
- package/templates/tad/fr/01-summary.md +17 -0
- package/templates/tad/fr/02-purpose.md +35 -0
- package/templates/tad/fr/03-context.md +17 -0
- package/templates/tad/fr/04-general.md +29 -0
- package/templates/tad/fr/05-functional.md +7 -0
- package/templates/tad/fr/06-data.md +71 -0
- package/templates/tad/fr/07-application.md +49 -0
- package/templates/tad/fr/08-ui.md +41 -0
- package/templates/tad/fr/09-integrations.md +53 -0
- package/templates/tad/fr/10-security.md +59 -0
- package/templates/tad/fr/11-reporting.md +13 -0
- package/templates/tad/fr/12-operations.md +25 -0
- package/templates/tad/fr/13-nfr.md +9 -0
- package/templates/tad/fr/14-decisions.md +3 -0
- package/templates/tad/fr/15-outlook.md +3 -0
- package/templates/tad/fr/99-appendices.md +119 -0
- package/templates/vendor/LICENSE-html-docx-js.txt +21 -0
- package/templates/vendor/LICENSE-html2canvas.txt +22 -0
- package/templates/vendor/LICENSE-mermaid.txt +21 -0
- package/templates/vendor/html-docx.js +13216 -0
- package/templates/vendor/html2canvas.min.js +20 -0
- package/templates/vendor/mermaid.min.js +2314 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/* TADForce — in-place editing, targeted comments, saving and PDF / DOCX export. */
|
|
2
|
+
(function () {
|
|
3
|
+
'use strict';
|
|
4
|
+
var I18N = (window.TAD_I18N && window.TAD_I18N.editor) || {};
|
|
5
|
+
var BRAND = window.TAD_BRAND || {};
|
|
6
|
+
function T(k, vars) {
|
|
7
|
+
var s = I18N[k] !== undefined ? I18N[k] : k;
|
|
8
|
+
return String(s).replace(/\{(\w+)\}/g, function (m, v) { return vars && vars[v] !== undefined ? vars[v] : m; });
|
|
9
|
+
}
|
|
10
|
+
function statusLabel(s) { return T('status_' + (s === 'resolved' ? 'resolved' : 'open')); }
|
|
11
|
+
|
|
12
|
+
var main = document.getElementById('main');
|
|
13
|
+
var zones = [document.querySelector('.cover'), document.querySelector('article.doc')];
|
|
14
|
+
var registryEl = document.getElementById('tad-comments');
|
|
15
|
+
var comments = [];
|
|
16
|
+
try { comments = JSON.parse(registryEl.textContent || '[]'); } catch (e) { comments = []; }
|
|
17
|
+
var editing = false, dirty = false, fileHandle = null, activeCid = null;
|
|
18
|
+
|
|
19
|
+
var $ = function (id) { return document.getElementById(id); };
|
|
20
|
+
var btnEdit = $('btnEdit'), btnComment = $('btnComment'), btnPanel = $('btnPanel'), btnSave = $('btnSave'), btnPrint = $('btnPrint');
|
|
21
|
+
var panel = $('cmtPanel'), list = $('cmtList'), countEl = $('cmtCount'), pop = $('cmtPop'), popText = $('cmtPopText'), menu = $('printMenu');
|
|
22
|
+
|
|
23
|
+
function toast(msg, ms) {
|
|
24
|
+
var t = $('toast'); t.textContent = msg; t.classList.add('on');
|
|
25
|
+
clearTimeout(toast._t); toast._t = setTimeout(function () { t.classList.remove('on'); }, ms || 2600);
|
|
26
|
+
}
|
|
27
|
+
function setDirty(v) { dirty = v; btnSave.classList.toggle('dirty', v); }
|
|
28
|
+
function inZone(node) {
|
|
29
|
+
var el = node && (node.nodeType === 1 ? node : node.parentElement);
|
|
30
|
+
return !!(el && zones.some(function (z) { return z && z.contains(el); }) && !el.closest('svg'));
|
|
31
|
+
}
|
|
32
|
+
function sectionOf(node) {
|
|
33
|
+
var el = node.nodeType === 1 ? node : node.parentElement;
|
|
34
|
+
var heads = Array.prototype.slice.call(document.querySelectorAll('.doc h1, .doc h2, .doc h3'));
|
|
35
|
+
var found = null;
|
|
36
|
+
heads.forEach(function (h) { if (h.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING || h.contains(el)) found = h; });
|
|
37
|
+
return found ? found.textContent.replace(/#$/, '').trim() : (el.closest('.cover') ? T('cover') : T('docStart'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ---------- Edit mode ---------- */
|
|
41
|
+
function setEditing(on) {
|
|
42
|
+
editing = on;
|
|
43
|
+
if (on && commentMode) setCommentMode(false);
|
|
44
|
+
zones.forEach(function (z) { if (z) z.contentEditable = on ? 'true' : 'false'; });
|
|
45
|
+
document.querySelectorAll('figure.diagram, .fig-body, .anchor').forEach(function (f) { f.contentEditable = 'false'; });
|
|
46
|
+
document.body.classList.toggle('editing', on);
|
|
47
|
+
btnEdit.innerHTML = on ? T('done') : T('edit');
|
|
48
|
+
if (on) toast(T('editToast'), 4000);
|
|
49
|
+
}
|
|
50
|
+
btnEdit.addEventListener('click', function () { setEditing(!editing); });
|
|
51
|
+
zones.forEach(function (z) {
|
|
52
|
+
if (!z) return;
|
|
53
|
+
z.addEventListener('input', function () {
|
|
54
|
+
var s = window.getSelection();
|
|
55
|
+
var el = s.anchorNode && (s.anchorNode.nodeType === 1 ? s.anchorNode : s.anchorNode.parentElement);
|
|
56
|
+
var blk = el && el.closest('p, li, td, th, h1, h2, h3, h4, figcaption, .kpi, .card, .layer, .meta div');
|
|
57
|
+
if (blk) blk.setAttribute('data-edited', '1');
|
|
58
|
+
setDirty(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/* ---------- Comments ---------- */
|
|
63
|
+
/* Flow: click « Comment » → select text (the input opens on mouse release)
|
|
64
|
+
or « Capture an area » → draw a rectangle captured as an image. */
|
|
65
|
+
var pending = null, commentMode = false, cap = null;
|
|
66
|
+
function setCommentMode(on) {
|
|
67
|
+
commentMode = on;
|
|
68
|
+
document.body.classList.toggle('commenting', on);
|
|
69
|
+
btnComment.classList.toggle('on', on);
|
|
70
|
+
if (on) { window.getSelection().removeAllRanges(); if (editing) setEditing(false); }
|
|
71
|
+
}
|
|
72
|
+
btnComment.addEventListener('click', function () { if (pop.classList.contains('on')) return; setCommentMode(!commentMode); });
|
|
73
|
+
$('cbarCancel').addEventListener('click', function () { setCommentMode(false); });
|
|
74
|
+
$('cbarCapture').addEventListener('click', function () { setCommentMode(false); startCapture(null); });
|
|
75
|
+
document.addEventListener('mouseup', function (e) {
|
|
76
|
+
if (!commentMode || e.target.closest('#cmtBar, #side, #cmtPanel, #cmtPop')) return;
|
|
77
|
+
setTimeout(function () {
|
|
78
|
+
var s = window.getSelection();
|
|
79
|
+
if (!s.rangeCount || s.isCollapsed || !inZone(s.anchorNode) || !inZone(s.focusNode) || !s.toString().trim()) return;
|
|
80
|
+
var range = s.getRangeAt(0).cloneRange();
|
|
81
|
+
pending = { range: range, quote: range.toString(), section: sectionOf(range.startContainer), image: null, anchor: null };
|
|
82
|
+
setCommentMode(false);
|
|
83
|
+
openPop(range.getBoundingClientRect());
|
|
84
|
+
}, 10);
|
|
85
|
+
});
|
|
86
|
+
function openPop(r) {
|
|
87
|
+
pop.style.top = Math.min(window.innerHeight - 360, Math.max(12, r.bottom + 8)) + 'px';
|
|
88
|
+
pop.style.left = Math.min(window.innerWidth - 380, Math.max(312, r.left)) + 'px';
|
|
89
|
+
$('cmtPopQuote').textContent = pending.range ? '« ' + excerpt(pending.quote, 140) + ' »' : T('zoneCaptured', { section: pending.section });
|
|
90
|
+
refreshPopImg();
|
|
91
|
+
if (!pop.classList.contains('on') && !pending.keepText) popText.value = '';
|
|
92
|
+
pending.keepText = false;
|
|
93
|
+
pop.classList.add('on'); popText.focus();
|
|
94
|
+
}
|
|
95
|
+
function closePop() { pop.classList.remove('on'); pending = null; window.getSelection().removeAllRanges(); }
|
|
96
|
+
$('cmtCancel').addEventListener('click', closePop);
|
|
97
|
+
$('cmtOk').addEventListener('click', addComment);
|
|
98
|
+
$('cmtAddCap').addEventListener('click', function () { pending.keepText = true; pop.classList.remove('on'); startCapture(pending); });
|
|
99
|
+
popText.addEventListener('keydown', function (e) {
|
|
100
|
+
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) addComment();
|
|
101
|
+
if (e.key === 'Escape') closePop();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
var ov = $('capOverlay'), box = $('capBox');
|
|
105
|
+
function startCapture(p) {
|
|
106
|
+
cap = { pending: p, drag: false };
|
|
107
|
+
ov.classList.add('on'); box.style.display = 'none';
|
|
108
|
+
}
|
|
109
|
+
function cancelCapture() {
|
|
110
|
+
ov.classList.remove('on'); box.style.display = 'none';
|
|
111
|
+
var p = cap && cap.pending; cap = null;
|
|
112
|
+
if (p) openPop({ bottom: window.innerHeight / 3, left: 340 });
|
|
113
|
+
}
|
|
114
|
+
ov.addEventListener('mousedown', function (e) {
|
|
115
|
+
e.preventDefault(); cap.drag = true; cap.x0 = e.clientX; cap.y0 = e.clientY;
|
|
116
|
+
box.style.display = 'block'; box.style.left = e.clientX + 'px'; box.style.top = e.clientY + 'px'; box.style.width = '0px'; box.style.height = '0px';
|
|
117
|
+
});
|
|
118
|
+
ov.addEventListener('mousemove', function (e) {
|
|
119
|
+
if (!cap || !cap.drag) return;
|
|
120
|
+
box.style.left = Math.min(e.clientX, cap.x0) + 'px'; box.style.top = Math.min(e.clientY, cap.y0) + 'px';
|
|
121
|
+
box.style.width = Math.abs(e.clientX - cap.x0) + 'px'; box.style.height = Math.abs(e.clientY - cap.y0) + 'px';
|
|
122
|
+
});
|
|
123
|
+
ov.addEventListener('mouseup', function (e) {
|
|
124
|
+
if (!cap || !cap.drag) return;
|
|
125
|
+
var r = { left: Math.min(e.clientX, cap.x0), top: Math.min(e.clientY, cap.y0), width: Math.abs(e.clientX - cap.x0), height: Math.abs(e.clientY - cap.y0) };
|
|
126
|
+
var p = cap.pending; cap = null;
|
|
127
|
+
ov.classList.remove('on'); box.style.display = 'none';
|
|
128
|
+
if (r.width < 12 || r.height < 12) { toast(T('zoneTooSmall')); startCapture(p); return; }
|
|
129
|
+
doCapture(r, p);
|
|
130
|
+
});
|
|
131
|
+
function blockAt(x, y) {
|
|
132
|
+
var el = document.elementFromPoint(x, y);
|
|
133
|
+
if (el && el.closest('svg')) el = el.closest('svg').parentElement;
|
|
134
|
+
if (!el || !inZone(el)) return null;
|
|
135
|
+
var b = el.closest('figure.diagram, .fig-body, .tw, .layers, .kpis, .cards, aside.callout, p, li, h1, h2, h3, h4, .meta div');
|
|
136
|
+
if (!b) return null;
|
|
137
|
+
if (b.matches('figure.diagram')) return b.querySelector('figcaption');
|
|
138
|
+
if (b.matches('.fig-body, .layers')) { var n = b.nextElementSibling; return n && n.matches('.figcap') ? n : b.parentElement; }
|
|
139
|
+
if (b.matches('.tw')) return b.querySelector('th, td');
|
|
140
|
+
if (b.matches('.kpis, .cards')) return b.firstElementChild;
|
|
141
|
+
return b;
|
|
142
|
+
}
|
|
143
|
+
/* The comment box opens immediately on mouse release; the image is rendered in the background
|
|
144
|
+
and attached as soon as it is ready (to the pending box or to the already-saved comment). */
|
|
145
|
+
function doCapture(r, p) {
|
|
146
|
+
if (!p) {
|
|
147
|
+
var target = blockAt(r.left + r.width / 2, r.top + Math.min(r.height / 2, 30));
|
|
148
|
+
p = { range: null, quote: '', section: target ? sectionOf(target) : 'Document', image: null, anchor: target };
|
|
149
|
+
}
|
|
150
|
+
p.capturing = true;
|
|
151
|
+
pending = p;
|
|
152
|
+
openPop({ bottom: Math.min(r.top + r.height, window.innerHeight - 380), left: r.left });
|
|
153
|
+
// Let the browser paint the comment box before the (CPU-heavy) rendering starts.
|
|
154
|
+
var plan = capturePlan(r);
|
|
155
|
+
new Promise(function (res) { requestAnimationFrame(function () { setTimeout(res, 30); }); })
|
|
156
|
+
.then(function () { return grab(r, plan); }).then(function (img) {
|
|
157
|
+
p.image = img; p.capturing = false;
|
|
158
|
+
if (pending === p) refreshPopImg();
|
|
159
|
+
if (p.cid) { comments.forEach(function (c) { if (c.id === p.cid) c.image = img; }); persist(); render(); setDirty(true); }
|
|
160
|
+
}, function (err) {
|
|
161
|
+
p.capturing = false; if (pending === p) refreshPopImg();
|
|
162
|
+
toast(T('captureFailed', { error: err && err.message }), 5000);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function refreshPopImg() {
|
|
166
|
+
$('cmtPopImg').innerHTML = pending.image ? '<img alt="' + T('captureAlt') + '" src="' + pending.image + '">' : (pending.capturing ? '<div class="cap-wait">' + T('captureWait') + '</div>' : '');
|
|
167
|
+
$('cmtAddCap').textContent = pending.image ? T('redoCapture') : T('attachCapture');
|
|
168
|
+
}
|
|
169
|
+
// Blocks of the document far from the zone: their content is neither cloned nor rendered
|
|
170
|
+
// (html2canvas otherwise copies the whole page, thousands of SVG nodes, synchronously).
|
|
171
|
+
function capturePlan(r) {
|
|
172
|
+
var near = function (el) { var b = el.getBoundingClientRect(); return b.bottom >= r.top - 60 && b.top <= r.top + r.height + 60; };
|
|
173
|
+
var far = new Set();
|
|
174
|
+
var plan = zones.map(function (z) {
|
|
175
|
+
if (!z) return null;
|
|
176
|
+
return Array.prototype.map.call(z.children, function (c) { if (near(c)) return null; far.add(c); return c.offsetHeight; });
|
|
177
|
+
});
|
|
178
|
+
plan.far = far;
|
|
179
|
+
// Page position of every kept block, used to re-align the clone exactly (see onclone).
|
|
180
|
+
plan.tops = zones.map(function (z, zi) {
|
|
181
|
+
if (!z || !plan[zi]) return null;
|
|
182
|
+
var px = z.getBoundingClientRect().left;
|
|
183
|
+
return Array.prototype.map.call(z.children, function (c, i) {
|
|
184
|
+
if (plan[zi][i] != null) return null;
|
|
185
|
+
var b = c.getBoundingClientRect(); return { top: b.top + window.scrollY, left: b.left - px, width: b.width };
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
return plan;
|
|
189
|
+
}
|
|
190
|
+
function grab(r, plan) {
|
|
191
|
+
if (typeof html2canvas === 'undefined') return Promise.reject(new Error(T('noCaptureLib')));
|
|
192
|
+
var far = plan.far;
|
|
193
|
+
return html2canvas(document.body, {
|
|
194
|
+
x: r.left + window.scrollX, y: r.top + window.scrollY, width: r.width, height: r.height,
|
|
195
|
+
// innerWidth includes the scrollbar: the clone then lays text out on exactly the same width as the page.
|
|
196
|
+
windowWidth: window.innerWidth, windowHeight: window.innerHeight,
|
|
197
|
+
scale: Math.min(2, window.devicePixelRatio || 1), backgroundColor: '#ffffff', logging: false,
|
|
198
|
+
ignoreElements: function (el) {
|
|
199
|
+
return far.has(el.parentElement) || el.tagName === 'SCRIPT' ||
|
|
200
|
+
/^(side|cmtPanel|cmtPop|toast|capOverlay|cmtBar|burger|lightbox)$/.test(el.id || '') || (el.classList && el.classList.contains('edit-banner'));
|
|
201
|
+
},
|
|
202
|
+
onclone: function (d) {
|
|
203
|
+
d.documentElement.style.scrollBehavior = 'auto';
|
|
204
|
+
[d.querySelector('.cover'), d.querySelector('article.doc')].forEach(function (z, zi) {
|
|
205
|
+
if (!z || !plan[zi]) return;
|
|
206
|
+
Array.prototype.forEach.call(z.children, function (c, i) {
|
|
207
|
+
var h = plan[zi][i];
|
|
208
|
+
if (h == null) return;
|
|
209
|
+
c.innerHTML = ''; c.style.boxSizing = 'border-box'; c.style.height = h + 'px'; c.style.minHeight = h + 'px';
|
|
210
|
+
});
|
|
211
|
+
// Any residual difference (margins dropped with emptied blocks, rounding) is cancelled by
|
|
212
|
+
// shifting each kept block back to its exact on-screen position.
|
|
213
|
+
Array.prototype.forEach.call(z.children, function (c, i) {
|
|
214
|
+
var o = plan.tops[zi][i];
|
|
215
|
+
if (!o) return;
|
|
216
|
+
c.style.width = o.width + 'px';
|
|
217
|
+
var dy = o.top - (c.getBoundingClientRect().top + d.defaultView.pageYOffset);
|
|
218
|
+
if (Math.abs(dy) > 0.5) { c.style.position = 'relative'; c.style.top = dy + 'px'; }
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
// The clone was scrolled while the emptied blocks had no height yet (scroll clamped):
|
|
222
|
+
// scroll it again now that the page has its real height, or deep zones are offset.
|
|
223
|
+
// The page uses smooth scrolling, which the clone inherits: force an instant jump, otherwise
|
|
224
|
+
// the clone is still mid-animation (or at the top) when html2canvas reads positions.
|
|
225
|
+
d.documentElement.style.scrollBehavior = 'auto';
|
|
226
|
+
d.defaultView.scrollTo({ left: window.scrollX, top: window.scrollY, behavior: 'instant' });
|
|
227
|
+
}
|
|
228
|
+
}).then(function (cv) {
|
|
229
|
+
var k = Math.min(1, 1100 / cv.width), o = document.createElement('canvas');
|
|
230
|
+
o.width = Math.round(cv.width * k); o.height = Math.round(cv.height * k);
|
|
231
|
+
var g = o.getContext('2d'); g.fillStyle = '#fff'; g.fillRect(0, 0, o.width, o.height); g.drawImage(cv, 0, 0, o.width, o.height);
|
|
232
|
+
return o.toDataURL('image/jpeg', 0.82);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function insertPin(el, cid) {
|
|
236
|
+
var s = document.createElement('span');
|
|
237
|
+
s.className = 'tad-pin'; s.setAttribute('data-cid', cid); s.setAttribute('contenteditable', 'false');
|
|
238
|
+
s.title = T('pinTitle'); s.textContent = '📷';
|
|
239
|
+
el.insertBefore(s, el.firstChild);
|
|
240
|
+
}
|
|
241
|
+
$('lightbox').addEventListener('click', function () { this.classList.remove('on'); });
|
|
242
|
+
|
|
243
|
+
function excerpt(t, n) { t = t.replace(/\s+/g, ' ').trim(); return t.length > n ? t.slice(0, n - 1) + '…' : t; }
|
|
244
|
+
function wrapRange(range, cid) {
|
|
245
|
+
var root = range.commonAncestorContainer;
|
|
246
|
+
var nodes = [];
|
|
247
|
+
if (root.nodeType === 3) nodes = [root];
|
|
248
|
+
else {
|
|
249
|
+
var w = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
250
|
+
acceptNode: function (n) {
|
|
251
|
+
return range.intersectsNode(n) && n.nodeValue.trim() && !n.parentElement.closest('svg') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
while (w.nextNode()) nodes.push(w.currentNode);
|
|
255
|
+
}
|
|
256
|
+
nodes.forEach(function (n) {
|
|
257
|
+
var start = n === range.startContainer ? range.startOffset : 0;
|
|
258
|
+
var end = n === range.endContainer ? range.endOffset : n.nodeValue.length;
|
|
259
|
+
if (start >= end) return;
|
|
260
|
+
var t = n;
|
|
261
|
+
if (start > 0) t = t.splitText(start);
|
|
262
|
+
if (end - start < t.nodeValue.length) t.splitText(end - start);
|
|
263
|
+
var m = document.createElement('mark');
|
|
264
|
+
m.className = 'tad-cmt'; m.setAttribute('data-cid', cid);
|
|
265
|
+
t.parentNode.insertBefore(m, t); m.appendChild(t);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function addComment() {
|
|
269
|
+
var txt = popText.value.trim();
|
|
270
|
+
if (!txt) { popText.focus(); return; }
|
|
271
|
+
var p = pending, cid = 'c' + Date.now().toString(36);
|
|
272
|
+
p.cid = cid;
|
|
273
|
+
if (p.range) wrapRange(p.range, cid); else if (p.anchor) insertPin(p.anchor, cid);
|
|
274
|
+
comments.push({ id: cid, kind: p.range ? 'text' : 'zone', comment: txt, quote: p.range ? excerpt(p.quote, 400) : '', section: p.section,
|
|
275
|
+
image: p.image || null, date: new Date().toISOString(), status: 'open' });
|
|
276
|
+
closePop();
|
|
277
|
+
persist(); render(); openPanel(true); setDirty(true);
|
|
278
|
+
toast(T('added'));
|
|
279
|
+
}
|
|
280
|
+
function persist() { registryEl.textContent = JSON.stringify(comments, null, 1).replace(/</g, '\\u003c'); }
|
|
281
|
+
function marksOf(cid) { return document.querySelectorAll('mark.tad-cmt[data-cid="' + cid + '"], .tad-pin[data-cid="' + cid + '"]'); }
|
|
282
|
+
function unwrap(m) {
|
|
283
|
+
if (m.classList.contains('tad-pin')) { m.remove(); return; }
|
|
284
|
+
var p = m.parentNode; while (m.firstChild) p.insertBefore(m.firstChild, m); p.removeChild(m); p.normalize();
|
|
285
|
+
}
|
|
286
|
+
function render() {
|
|
287
|
+
var open = comments.filter(function (c) { return c.status !== 'resolved'; }).length;
|
|
288
|
+
countEl.textContent = comments.length ? (open + '/' + comments.length) : '0';
|
|
289
|
+
var edited = document.querySelectorAll('[data-edited]').length;
|
|
290
|
+
$('cmtEdited').textContent = edited ? T('edited', { n: edited }) : '';
|
|
291
|
+
if (!comments.length) { list.innerHTML = '<p class="cmt-empty">' + T('empty') + '</p>'; return; }
|
|
292
|
+
list.innerHTML = '';
|
|
293
|
+
comments.forEach(function (c, i) {
|
|
294
|
+
var li = document.createElement('div');
|
|
295
|
+
li.className = 'cmt-item' + (c.status === 'resolved' ? ' done' : '') + (c.id === activeCid ? ' active' : '');
|
|
296
|
+
li.innerHTML = '<div class="cmt-h"><b>#' + (i + 1) + '</b> <span></span><em></em></div><blockquote></blockquote><div class="cmt-t" contenteditable="true"></div>' +
|
|
297
|
+
'<div class="cmt-a"><button data-a="go"></button><button data-a="st"></button><button data-a="del"></button></div>';
|
|
298
|
+
li.querySelector('.cmt-h span').textContent = c.section;
|
|
299
|
+
li.querySelector('.cmt-h em').textContent = statusLabel(c.status);
|
|
300
|
+
li.querySelector('[data-a="go"]').textContent = T('view');
|
|
301
|
+
li.querySelector('[data-a="del"]').textContent = T('delete');
|
|
302
|
+
var bq = li.querySelector('blockquote');
|
|
303
|
+
if (c.quote) bq.textContent = c.quote; else bq.remove();
|
|
304
|
+
if (c.image) {
|
|
305
|
+
var im = document.createElement('img'); im.className = 'cmt-img'; im.src = c.image; im.alt = T('captureAlt'); im.title = T('enlarge');
|
|
306
|
+
im.addEventListener('click', function (ev) { ev.stopPropagation(); $('lightboxImg').src = c.image; $('lightbox').classList.add('on'); });
|
|
307
|
+
li.insertBefore(im, li.querySelector('.cmt-t'));
|
|
308
|
+
}
|
|
309
|
+
li.querySelector('.cmt-t').textContent = c.comment;
|
|
310
|
+
if (c.response) { var rp = document.createElement('div'); rp.className = 'cmt-r'; rp.textContent = T('handled', { response: c.response }); li.insertBefore(rp, li.querySelector('.cmt-a')); }
|
|
311
|
+
li.querySelector('[data-a="st"]').textContent = c.status === 'resolved' ? T('reopen') : T('resolve');
|
|
312
|
+
li.querySelector('.cmt-t').addEventListener('input', function (e) { c.comment = e.target.textContent; persist(); setDirty(true); });
|
|
313
|
+
li.addEventListener('click', function (e) {
|
|
314
|
+
var a = e.target.getAttribute('data-a'); if (!a) return;
|
|
315
|
+
if (a === 'go') focusComment(c.id);
|
|
316
|
+
if (a === 'st') { c.status = c.status === 'resolved' ? 'open' : 'resolved'; marksOf(c.id).forEach(function (m) { m.classList.toggle('resolved', c.status === 'resolved'); }); }
|
|
317
|
+
if (a === 'del') { if (!confirm(T('confirmDelete'))) return; marksOf(c.id).forEach(unwrap); comments = comments.filter(function (x) { return x.id !== c.id; }); }
|
|
318
|
+
if (a !== 'go') { persist(); render(); setDirty(true); }
|
|
319
|
+
});
|
|
320
|
+
list.appendChild(li);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function focusComment(cid) {
|
|
324
|
+
activeCid = cid;
|
|
325
|
+
document.querySelectorAll('mark.tad-cmt.flash').forEach(function (m) { m.classList.remove('flash'); });
|
|
326
|
+
var ms = marksOf(cid);
|
|
327
|
+
if (ms.length) { ms[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); ms.forEach(function (m) { m.classList.add('flash'); }); }
|
|
328
|
+
render();
|
|
329
|
+
}
|
|
330
|
+
function openPanel(on) { document.body.classList.toggle('panel-open', on === undefined ? !document.body.classList.contains('panel-open') : on); }
|
|
331
|
+
btnPanel.addEventListener('click', function () { openPanel(); render(); });
|
|
332
|
+
$('cmtClose').addEventListener('click', function () { openPanel(false); });
|
|
333
|
+
main.addEventListener('click', function (e) {
|
|
334
|
+
var m = e.target.closest && e.target.closest('mark.tad-cmt, .tad-pin');
|
|
335
|
+
if (m && !editing && !commentMode) { openPanel(true); focusComment(m.getAttribute('data-cid')); }
|
|
336
|
+
});
|
|
337
|
+
$('cmtCopy').addEventListener('click', function () {
|
|
338
|
+
var open = comments.filter(function (c) { return c.status !== 'resolved'; });
|
|
339
|
+
var lines = [T('listTitle', { title: document.title, n: open.length }), ''];
|
|
340
|
+
open.forEach(function (c, i) {
|
|
341
|
+
lines.push((i + 1) + '. [' + c.section + '] ' + (c.quote ? '« ' + excerpt(c.quote, 200) + ' »' : T('capturedZone')) + (c.image ? T('captureInFile') : ''), ' → ' + c.comment, '');
|
|
342
|
+
});
|
|
343
|
+
var txt = lines.join('\n');
|
|
344
|
+
(navigator.clipboard ? navigator.clipboard.writeText(txt) : Promise.reject()).then(function () { toast(T('copied')); }, function () { prompt(T('copyPrompt'), txt); });
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
/* ---------- Saving ---------- */
|
|
348
|
+
function fileName() { var n = decodeURIComponent(location.pathname.split('/').pop() || ''); return /\.html?$/i.test(n) ? n : T('defaultFile'); }
|
|
349
|
+
function serialize() {
|
|
350
|
+
persist();
|
|
351
|
+
var clone = document.documentElement.cloneNode(true);
|
|
352
|
+
clone.querySelectorAll('[contenteditable]').forEach(function (e) { e.removeAttribute('contenteditable'); });
|
|
353
|
+
clone.querySelectorAll('.flash, .on').forEach(function (e) { e.classList.remove('flash', 'on'); });
|
|
354
|
+
var b = clone.querySelector('body'); b.classList.remove('editing', 'nav-open', 'panel-open', 'commenting');
|
|
355
|
+
clone.querySelector('#cmtPopImg').innerHTML = ''; clone.querySelector('#lightboxImg').removeAttribute('src');
|
|
356
|
+
clone.querySelector('#toast').textContent = '';
|
|
357
|
+
clone.querySelector('#cmtList').innerHTML = '';
|
|
358
|
+
return '<!doctype html>\n' + clone.outerHTML;
|
|
359
|
+
}
|
|
360
|
+
function download(blob, name) {
|
|
361
|
+
var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name;
|
|
362
|
+
document.body.appendChild(a); a.click(); setTimeout(function () { URL.revokeObjectURL(a.href); a.remove(); }, 1500);
|
|
363
|
+
}
|
|
364
|
+
// Opened through `tadforce review` / `build --open`: the local server writes back to the same file.
|
|
365
|
+
var served = location.protocol === 'http:' && location.hostname === '127.0.0.1';
|
|
366
|
+
async function save() {
|
|
367
|
+
var html = serialize();
|
|
368
|
+
if (served) {
|
|
369
|
+
try {
|
|
370
|
+
var r = await fetch(location.pathname, { method: 'PUT', headers: { 'content-type': 'text/html; charset=utf-8' }, body: html });
|
|
371
|
+
if (r.ok) { setDirty(false); toast(T('savedServer', { name: fileName() }), 3500); return; }
|
|
372
|
+
} catch (e) { /* server stopped: fall back to the file picker */ }
|
|
373
|
+
}
|
|
374
|
+
if (window.showSaveFilePicker) {
|
|
375
|
+
try {
|
|
376
|
+
if (!fileHandle) {
|
|
377
|
+
toast(T('pickFile'), 4000);
|
|
378
|
+
fileHandle = await window.showSaveFilePicker({ suggestedName: fileName(), types: [{ description: T('htmlPage'), accept: { 'text/html': ['.html'] } }] });
|
|
379
|
+
}
|
|
380
|
+
var w = await fileHandle.createWritable(); await w.write(html); await w.close();
|
|
381
|
+
setDirty(false); toast(T('saved', { name: fileHandle.name })); return;
|
|
382
|
+
} catch (e) { if (e && e.name === 'AbortError') return; fileHandle = null; }
|
|
383
|
+
}
|
|
384
|
+
download(new Blob([html], { type: 'text/html;charset=utf-8' }), fileName());
|
|
385
|
+
setDirty(false); toast(T('downloaded', { name: fileName() }), 5000);
|
|
386
|
+
}
|
|
387
|
+
btnSave.addEventListener('click', save);
|
|
388
|
+
document.addEventListener('keydown', function (e) {
|
|
389
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') { e.preventDefault(); save(); }
|
|
390
|
+
if (e.key === 'Escape') {
|
|
391
|
+
if (ov.classList.contains('on')) cancelCapture();
|
|
392
|
+
else if (commentMode) setCommentMode(false);
|
|
393
|
+
else if (editing && !pop.classList.contains('on')) setEditing(false);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
window.addEventListener('beforeunload', function (e) { if (dirty) { e.preventDefault(); e.returnValue = ''; } });
|
|
397
|
+
|
|
398
|
+
/* ---------- Print / export ---------- */
|
|
399
|
+
btnPrint.addEventListener('click', function (e) { e.stopPropagation(); menu.classList.toggle('on'); });
|
|
400
|
+
document.addEventListener('click', function (e) { if (!menu.contains(e.target)) menu.classList.remove('on'); });
|
|
401
|
+
$('doPdf').addEventListener('click', function () { menu.classList.remove('on'); if (editing) setEditing(false); setTimeout(function () { window.print(); }, 50); });
|
|
402
|
+
$('doDocx').addEventListener('click', function () { menu.classList.remove('on'); exportDocx(); });
|
|
403
|
+
|
|
404
|
+
function svgToPng(svg, maxW) {
|
|
405
|
+
return new Promise(function (resolve) {
|
|
406
|
+
var vb = svg.viewBox && svg.viewBox.baseVal, bb = svg.getBoundingClientRect();
|
|
407
|
+
var w = (vb && vb.width) || bb.width || 800, h = (vb && vb.height) || bb.height || 400;
|
|
408
|
+
var c = svg.cloneNode(true);
|
|
409
|
+
c.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); c.setAttribute('width', w); c.setAttribute('height', h);
|
|
410
|
+
c.style.maxWidth = ''; c.removeAttribute('style');
|
|
411
|
+
if (svg.getAttribute('style') && /font-family/.test(svg.getAttribute('style'))) c.setAttribute('style', 'font-family:Segoe UI,Arial,sans-serif');
|
|
412
|
+
var url = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(new XMLSerializer().serializeToString(c));
|
|
413
|
+
var img = new Image();
|
|
414
|
+
img.onload = function () {
|
|
415
|
+
var scale = Math.min(1.6, 1500 / w), cv = document.createElement('canvas'); cv.width = Math.round(w * scale); cv.height = Math.round(h * scale);
|
|
416
|
+
var ctx = cv.getContext('2d'); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, cv.width, cv.height); ctx.drawImage(img, 0, 0, cv.width, cv.height);
|
|
417
|
+
var dw = Math.min(maxW, w); resolve({ src: cv.toDataURL('image/png'), w: Math.round(dw), h: Math.round(dw * h / w) });
|
|
418
|
+
};
|
|
419
|
+
img.onerror = function () { resolve(null); };
|
|
420
|
+
img.src = url;
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
function imgToPng(src, maxW) {
|
|
424
|
+
return new Promise(function (resolve) {
|
|
425
|
+
var img = new Image();
|
|
426
|
+
img.onload = function () {
|
|
427
|
+
var w = img.naturalWidth || 300, h = img.naturalHeight || 300, cv = document.createElement('canvas'); cv.width = w; cv.height = h;
|
|
428
|
+
var ctx = cv.getContext('2d'); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, w, h); ctx.drawImage(img, 0, 0, w, h);
|
|
429
|
+
var dw = Math.min(maxW, w); resolve({ src: cv.toDataURL('image/png'), w: dw, h: Math.round(dw * h / w) });
|
|
430
|
+
};
|
|
431
|
+
img.onerror = function () { resolve(null); }; img.src = src;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
function el(tag, html) { var e = document.createElement(tag); if (html !== undefined) e.innerHTML = html; return e; }
|
|
435
|
+
function tableOf(rows, cls) {
|
|
436
|
+
var t = el('table'); t.className = cls || '';
|
|
437
|
+
rows.forEach(function (r) { var tr = t.insertRow(); r.forEach(function (c) { var td = tr.insertCell(); td.innerHTML = c; }); });
|
|
438
|
+
return t;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function exportDocx() {
|
|
442
|
+
if (typeof htmlDocx === 'undefined') { toast(T('docxMissing')); return; }
|
|
443
|
+
if (editing) setEditing(false);
|
|
444
|
+
toast(T('docxRunning'), 8000);
|
|
445
|
+
var live = main.querySelectorAll('svg'), src = main.cloneNode(true), cl = src.querySelectorAll('svg');
|
|
446
|
+
for (var i = 0; i < live.length; i++) {
|
|
447
|
+
var png = await svgToPng(live[i], 620);
|
|
448
|
+
if (png && cl[i]) { var im = el('img'); im.src = png.src; im.width = png.w; im.height = png.h; cl[i].parentNode.replaceChild(im, cl[i]); }
|
|
449
|
+
}
|
|
450
|
+
var imgs = src.querySelectorAll('.cover-logos img');
|
|
451
|
+
for (var k = 0; k < imgs.length; k++) { var p = await imgToPng(imgs[k].src, 110); if (p) { imgs[k].src = p.src; imgs[k].width = p.w; imgs[k].height = p.h; } }
|
|
452
|
+
src.querySelectorAll('.anchor, script, .doc-foot, .pending').forEach(function (e) { e.remove(); });
|
|
453
|
+
src.querySelectorAll('mark.tad-cmt, .tad-pin').forEach(unwrap);
|
|
454
|
+
src.querySelectorAll('[data-edited]').forEach(function (e) { e.removeAttribute('data-edited'); });
|
|
455
|
+
src.querySelectorAll('.tw').forEach(function (w) { w.replaceWith(w.firstElementChild); });
|
|
456
|
+
src.querySelectorAll('.kpis').forEach(function (k) {
|
|
457
|
+
var cells = Array.prototype.map.call(k.querySelectorAll('.kpi'), function (x) { return '<p class="kb">' + x.querySelector('b').innerHTML + '</p><p class="ks">' + x.querySelector('span').innerHTML + '</p>'; });
|
|
458
|
+
var rows = []; for (var j = 0; j < cells.length; j += 4) rows.push(cells.slice(j, j + 4));
|
|
459
|
+
k.replaceWith(tableOf(rows, 'kpit'));
|
|
460
|
+
});
|
|
461
|
+
src.querySelectorAll('.cards').forEach(function (k) {
|
|
462
|
+
var cells = Array.prototype.map.call(k.querySelectorAll('.card'), function (x) { return x.innerHTML; });
|
|
463
|
+
var rows = []; for (var j = 0; j < cells.length; j += 2) rows.push(cells.slice(j, j + 2));
|
|
464
|
+
k.replaceWith(tableOf(rows, 'cardt'));
|
|
465
|
+
});
|
|
466
|
+
src.querySelectorAll('.layers').forEach(function (k) {
|
|
467
|
+
k.replaceWith(tableOf(Array.prototype.map.call(k.querySelectorAll('.layer'), function (l) {
|
|
468
|
+
return ['<b>' + l.querySelector('.ln').textContent + '</b>', Array.prototype.map.call(l.querySelectorAll('.li span'), function (s) { return s.textContent; }).join(' · ')];
|
|
469
|
+
}), 'layert'));
|
|
470
|
+
});
|
|
471
|
+
src.querySelectorAll('.legend').forEach(function (k) { k.replaceWith(el('p', Array.prototype.map.call(k.querySelectorAll('.lg'), function (s) { return '■ ' + s.textContent; }).join(' '))); });
|
|
472
|
+
src.querySelectorAll('aside.callout').forEach(function (k) { var t = tableOf([[k.innerHTML]], 'calloutt'); k.replaceWith(t); });
|
|
473
|
+
src.querySelectorAll('.cover .meta').forEach(function (k) {
|
|
474
|
+
k.replaceWith(tableOf(Array.prototype.map.call(k.querySelectorAll('div'), function (d) { return ['<b>' + d.querySelector('b').textContent + '</b>', d.querySelector('span').innerHTML]; }), 'metat'));
|
|
475
|
+
});
|
|
476
|
+
src.querySelectorAll('.cover-logos').forEach(function (k) { k.replaceWith(tableOf([[k.children[0] ? k.children[0].outerHTML : '', k.children[1] ? '<p style="text-align:right">' + k.children[1].outerHTML + '</p>' : '']], 'logot')); });
|
|
477
|
+
src.querySelectorAll('figure.diagram').forEach(function (f) {
|
|
478
|
+
var d = el('div'); d.innerHTML = '<p class="fig">' + (f.querySelector('img') ? f.querySelector('img').outerHTML : '') + '</p><p class="figcap">' + f.querySelector('figcaption').innerHTML + '</p>'; f.replaceWith(d);
|
|
479
|
+
});
|
|
480
|
+
src.querySelectorAll('.fig-body').forEach(function (f) { var d = el('p'); d.className = 'fig'; d.innerHTML = f.innerHTML; f.replaceWith(d); });
|
|
481
|
+
var ink = BRAND.primary || '#0B2545', th = BRAND.tableHeader || '#0f2a4d', acc = BRAND.accent2 || '#7B3FE4';
|
|
482
|
+
var css = 'body{font-family:Calibri,"Segoe UI",Arial,sans-serif;font-size:10pt;color:#18212d}' +
|
|
483
|
+
'h1{font-size:18pt;color:' + ink + ';page-break-before:always;margin:0 0 8pt}h2{font-size:13.5pt;color:' + ink + ';margin:14pt 0 6pt}h3{font-size:11.5pt;margin:10pt 0 4pt}h4{font-size:10.5pt;color:' + ink + ';margin:0 0 3pt}' +
|
|
484
|
+
'.num{color:' + acc + '}p,li{margin:0 0 5pt;line-height:1.35}table{border-collapse:collapse;width:100%;margin:4pt 0 9pt}' +
|
|
485
|
+
'th{background:' + th + ';color:#ffffff;font-weight:bold;border:1px solid ' + th + ';padding:2pt 4pt;font-size:8.5pt;text-align:left;line-height:1.15}td{border:1px solid #cfd8e5;padding:2pt 4pt;font-size:8.5pt;vertical-align:top;line-height:1.15}td p{margin:0 0 2pt}' +
|
|
486
|
+
'code{font-family:Consolas,monospace;font-size:8pt;color:#3b2a8f}th code{color:#ffffff}.fig{text-align:center;margin:6pt 0 2pt}.figcap{text-align:center;font-style:italic;color:#5a6878;font-size:8.5pt;margin-bottom:10pt}' +
|
|
487
|
+
'table.kpit td,table.cardt td{border:1px solid #d6dde8;background:#f7f9fc;padding:6pt}.kb{font-size:16pt;font-weight:bold;color:' + ink + ';margin:0}.ks{font-size:8.5pt;color:#5a6878;margin:0}' +
|
|
488
|
+
'table.layert td{background:#E8F1FF;border:1px solid #c9d8f2}table.calloutt td{background:#f3efff;border:0;border-left:4px solid #8b5cf6;padding:6pt 8pt}.ct{font-weight:bold;color:#6d3fe0;font-size:8.5pt;text-transform:uppercase}' +
|
|
489
|
+
'table.metat td{border:1px solid #d6dde8}table.logot td{border:0}.kicker{color:' + acc + ';font-weight:bold;font-size:10pt;text-transform:uppercase;letter-spacing:1pt}.ctitle{font-size:26pt;color:' + ink + ';page-break-before:auto}.csub{font-size:13pt;color:#5a6878}.conf{font-size:8.5pt;color:#5a6878}.b{font-weight:bold}';
|
|
490
|
+
var cover = src.querySelector('.cover'), doc = src.querySelector('article.doc');
|
|
491
|
+
var body = (cover ? cover.innerHTML : '') + (doc ? doc.innerHTML : '');
|
|
492
|
+
var html = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' + document.title + '</title><style>' + css + '</style></head><body>' + body + '</body></html>';
|
|
493
|
+
try {
|
|
494
|
+
var blob = htmlDocx.asBlob(html, { orientation: 'portrait', margins: { top: 1000, right: 900, bottom: 1000, left: 900 } });
|
|
495
|
+
download(blob, fileName().replace(/\.html?$/i, '') + '.docx');
|
|
496
|
+
toast(T('docxDone'), 4000);
|
|
497
|
+
} catch (e) { toast(T('docxFailed', { error: e.message }), 5000); }
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
comments.forEach(function (c) { if (c.status === 'resolved') marksOf(c.id).forEach(function (m) { m.classList.add('resolved'); }); });
|
|
501
|
+
render();
|
|
502
|
+
})();
|