legal-markdown-js 0.1.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/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/browser.d.ts +87 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +157 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/index.d.ts +34 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +221 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/service.d.ts +128 -0
- package/dist/cli/service.d.ts.map +1 -0
- package/dist/cli/service.js +284 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/constants/index.d.ts +229 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +232 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/core/exporters/metadata-exporter.d.ts +104 -0
- package/dist/core/exporters/metadata-exporter.d.ts.map +1 -0
- package/dist/core/exporters/metadata-exporter.js +201 -0
- package/dist/core/exporters/metadata-exporter.js.map +1 -0
- package/dist/core/index.d.ts +40 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +56 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/parsers/yaml-parser.d.ts +149 -0
- package/dist/core/parsers/yaml-parser.d.ts.map +1 -0
- package/dist/core/parsers/yaml-parser.js +321 -0
- package/dist/core/parsers/yaml-parser.js.map +1 -0
- package/dist/core/processors/clause-processor.d.ts +74 -0
- package/dist/core/processors/clause-processor.d.ts.map +1 -0
- package/dist/core/processors/clause-processor.js +213 -0
- package/dist/core/processors/clause-processor.js.map +1 -0
- package/dist/core/processors/date-processor.d.ts +90 -0
- package/dist/core/processors/date-processor.d.ts.map +1 -0
- package/dist/core/processors/date-processor.js +336 -0
- package/dist/core/processors/date-processor.js.map +1 -0
- package/dist/core/processors/header-processor.d.ts +104 -0
- package/dist/core/processors/header-processor.d.ts.map +1 -0
- package/dist/core/processors/header-processor.js +585 -0
- package/dist/core/processors/header-processor.js.map +1 -0
- package/dist/core/processors/import-processor.d.ts +116 -0
- package/dist/core/processors/import-processor.d.ts.map +1 -0
- package/dist/core/processors/import-processor.js +236 -0
- package/dist/core/processors/import-processor.js.map +1 -0
- package/dist/core/processors/mixin-processor.d.ts +93 -0
- package/dist/core/processors/mixin-processor.d.ts.map +1 -0
- package/dist/core/processors/mixin-processor.js +378 -0
- package/dist/core/processors/mixin-processor.js.map +1 -0
- package/dist/core/processors/reference-processor.d.ts +115 -0
- package/dist/core/processors/reference-processor.d.ts.map +1 -0
- package/dist/core/processors/reference-processor.js +273 -0
- package/dist/core/processors/reference-processor.js.map +1 -0
- package/dist/errors/index.d.ts +234 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +267 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/extensions/batch-processor.d.ts +197 -0
- package/dist/extensions/batch-processor.d.ts.map +1 -0
- package/dist/extensions/batch-processor.js +392 -0
- package/dist/extensions/batch-processor.js.map +1 -0
- package/dist/extensions/formatters/index.d.ts +99 -0
- package/dist/extensions/formatters/index.d.ts.map +1 -0
- package/dist/extensions/formatters/index.js +128 -0
- package/dist/extensions/formatters/index.js.map +1 -0
- package/dist/extensions/index.d.ts +53 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/latex-parser.d.ts +111 -0
- package/dist/extensions/latex-parser.d.ts.map +1 -0
- package/dist/extensions/latex-parser.js +563 -0
- package/dist/extensions/latex-parser.js.map +1 -0
- package/dist/extensions/rst-parser.d.ts +112 -0
- package/dist/extensions/rst-parser.d.ts.map +1 -0
- package/dist/extensions/rst-parser.js +268 -0
- package/dist/extensions/rst-parser.js.map +1 -0
- package/dist/extensions/template-loops.d.ts +61 -0
- package/dist/extensions/template-loops.d.ts.map +1 -0
- package/dist/extensions/template-loops.js +418 -0
- package/dist/extensions/template-loops.js.map +1 -0
- package/dist/extensions/utilities/index.d.ts +114 -0
- package/dist/extensions/utilities/index.d.ts.map +1 -0
- package/dist/extensions/utilities/index.js +137 -0
- package/dist/extensions/utilities/index.js.map +1 -0
- package/dist/extensions/validators/index.d.ts +65 -0
- package/dist/extensions/validators/index.d.ts.map +1 -0
- package/dist/extensions/validators/index.js +88 -0
- package/dist/extensions/validators/index.js.map +1 -0
- package/dist/generators/html-generator.d.ts +184 -0
- package/dist/generators/html-generator.d.ts.map +1 -0
- package/dist/generators/html-generator.js +361 -0
- package/dist/generators/html-generator.js.map +1 -0
- package/dist/generators/pdf-generator.d.ts +165 -0
- package/dist/generators/pdf-generator.d.ts.map +1 -0
- package/dist/generators/pdf-generator.js +275 -0
- package/dist/generators/pdf-generator.js.map +1 -0
- package/dist/helpers/date-helpers.d.ts +216 -0
- package/dist/helpers/date-helpers.d.ts.map +1 -0
- package/dist/helpers/date-helpers.js +402 -0
- package/dist/helpers/date-helpers.js.map +1 -0
- package/dist/helpers/index.d.ts +87 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +149 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/number-helpers.d.ts +269 -0
- package/dist/helpers/number-helpers.d.ts.map +1 -0
- package/dist/helpers/number-helpers.js +406 -0
- package/dist/helpers/number-helpers.js.map +1 -0
- package/dist/helpers/string-helpers.d.ts +391 -0
- package/dist/helpers/string-helpers.d.ts.map +1 -0
- package/dist/helpers/string-helpers.js +549 -0
- package/dist/helpers/string-helpers.js.map +1 -0
- package/dist/index.d.ts +229 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +384 -0
- package/dist/index.js.map +1 -0
- package/dist/legal-markdown.umd.min.js +2 -0
- package/dist/legal-markdown.umd.min.js.LICENSE.txt +14 -0
- package/dist/legal-markdown.umd.min.js.map +1 -0
- package/dist/lib/index.d.ts +150 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +265 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/parsers/content-detector.d.ts +131 -0
- package/dist/parsers/content-detector.d.ts.map +1 -0
- package/dist/parsers/content-detector.js +220 -0
- package/dist/parsers/content-detector.js.map +1 -0
- package/dist/parsers/fallback-parsers.d.ts +14 -0
- package/dist/parsers/fallback-parsers.d.ts.map +1 -0
- package/dist/parsers/fallback-parsers.js +188 -0
- package/dist/parsers/fallback-parsers.js.map +1 -0
- package/dist/parsers/implementations/pandoc-native.d.ts +13 -0
- package/dist/parsers/implementations/pandoc-native.d.ts.map +1 -0
- package/dist/parsers/implementations/pandoc-native.js +63 -0
- package/dist/parsers/implementations/pandoc-native.js.map +1 -0
- package/dist/parsers/implementations/pandoc-wasm.d.ts +14 -0
- package/dist/parsers/implementations/pandoc-wasm.d.ts.map +1 -0
- package/dist/parsers/implementations/pandoc-wasm.js +64 -0
- package/dist/parsers/implementations/pandoc-wasm.js.map +1 -0
- package/dist/parsers/pandoc-factory.d.ts +97 -0
- package/dist/parsers/pandoc-factory.d.ts.map +1 -0
- package/dist/parsers/pandoc-factory.js +146 -0
- package/dist/parsers/pandoc-factory.js.map +1 -0
- package/dist/parsers/pandoc-loader.d.ts +24 -0
- package/dist/parsers/pandoc-loader.d.ts.map +1 -0
- package/dist/parsers/pandoc-loader.js +124 -0
- package/dist/parsers/pandoc-loader.js.map +1 -0
- package/dist/parsers/pandoc-parser.d.ts +27 -0
- package/dist/parsers/pandoc-parser.d.ts.map +1 -0
- package/dist/parsers/pandoc-parser.js +3 -0
- package/dist/parsers/pandoc-parser.js.map +1 -0
- package/dist/styles/default.css +125 -0
- package/dist/styles/headers.css +146 -0
- package/dist/styles/highlight.css +171 -0
- package/dist/tracking/field-tracker.d.ts +206 -0
- package/dist/tracking/field-tracker.d.ts.map +1 -0
- package/dist/tracking/field-tracker.js +247 -0
- package/dist/tracking/field-tracker.js.map +1 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/logger.d.ts +107 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +122 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/web/bundle-standalone.js +28 -0
- package/dist/web/bundle.js +17 -0
- package/dist/web/index.html +1465 -0
- package/dist/web/legal-markdown.umd.min.js +2 -0
- package/dist/web/standalone.html +390 -0
- package/dist/web/styles.css +874 -0
- package/package.json +118 -0
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Legal Markdown JS - Playground</title>
|
|
8
|
+
<link rel="stylesheet" href="styles.css">
|
|
9
|
+
</head>
|
|
10
|
+
|
|
11
|
+
<body>
|
|
12
|
+
<div class="app-container">
|
|
13
|
+
<header class="app-header">
|
|
14
|
+
<h1>Legal Markdown JS - Playground</h1>
|
|
15
|
+
<div class="header-actions">
|
|
16
|
+
<button class="theme-toggle" title="Toggle theme">🌙</button>
|
|
17
|
+
<button class="help-btn" title="Help">❓</button>
|
|
18
|
+
</div>
|
|
19
|
+
</header>
|
|
20
|
+
|
|
21
|
+
<main class="editor-container">
|
|
22
|
+
<div class="input-panel">
|
|
23
|
+
<div class="panel-header">
|
|
24
|
+
<h3>Input</h3>
|
|
25
|
+
<div class="panel-actions">
|
|
26
|
+
<label class="file-input-label">
|
|
27
|
+
📁 Upload File
|
|
28
|
+
<input type="file" class="file-input" accept=".md,.txt" style="display: none;">
|
|
29
|
+
</label>
|
|
30
|
+
<div class="example-dropdown">
|
|
31
|
+
<select class="example-select">
|
|
32
|
+
<option value="">📄 Examples</option>
|
|
33
|
+
<option value="service-agreement">Service Agreement</option>
|
|
34
|
+
<option value="lease-contract">Lease Contract</option>
|
|
35
|
+
<option value="purchase-ticket">Purchase Ticket</option>
|
|
36
|
+
<option value="nda">Non-Disclosure Agreement</option>
|
|
37
|
+
<option value="employment-contract">Employment Contract</option>
|
|
38
|
+
</select>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<textarea class="editor" placeholder="Enter your Legal Markdown content here...
|
|
43
|
+
|
|
44
|
+
Select an example from the dropdown above to get started, or write your own content using YAML front matter and Legal Markdown syntax.
|
|
45
|
+
|
|
46
|
+
Example structure:
|
|
47
|
+
---
|
|
48
|
+
title: Document Title
|
|
49
|
+
variable: value
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# {{title}}
|
|
53
|
+
|
|
54
|
+
Your content with {{variable}} substitutions..."></textarea>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="column-resizer" id="resizer1"></div>
|
|
58
|
+
|
|
59
|
+
<!-- CSS Editor Panel -->
|
|
60
|
+
<div class="css-panel">
|
|
61
|
+
<div class="panel-header">
|
|
62
|
+
<h3>Custom CSS</h3>
|
|
63
|
+
<div class="panel-actions">
|
|
64
|
+
<label class="file-input-label">
|
|
65
|
+
📁 Upload CSS
|
|
66
|
+
<input type="file" class="css-file-input" accept=".css" style="display: none;">
|
|
67
|
+
</label>
|
|
68
|
+
<div class="example-dropdown">
|
|
69
|
+
<select class="css-example-select">
|
|
70
|
+
<option value="">🎨 CSS Examples</option>
|
|
71
|
+
<option value="modern">Modern Style</option>
|
|
72
|
+
<option value="classic">Classic Legal</option>
|
|
73
|
+
<option value="minimal">Minimal</option>
|
|
74
|
+
</select>
|
|
75
|
+
</div>
|
|
76
|
+
<button class="css-reset secondary">🔄</button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="css-content">
|
|
80
|
+
<textarea class="css-editor" placeholder="Enter your custom CSS here...
|
|
81
|
+
|
|
82
|
+
/* Example: Change document colors */
|
|
83
|
+
body {
|
|
84
|
+
background: #f9f9f9;
|
|
85
|
+
color: #333;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
h1 {
|
|
89
|
+
color: #2c3e50;
|
|
90
|
+
border-bottom: 2px solid #3498db;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Example: Customize spacing */
|
|
94
|
+
p {
|
|
95
|
+
margin-bottom: 1.2em;
|
|
96
|
+
line-height: 1.6;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Your custom styles here... */"></textarea>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="column-resizer" id="resizer2"></div>
|
|
104
|
+
|
|
105
|
+
<div class="output-panel">
|
|
106
|
+
<div class="panel-header">
|
|
107
|
+
<h3>Preview</h3>
|
|
108
|
+
<div class="panel-actions">
|
|
109
|
+
<button class="toggle-styles-btn" title="Toggle base styles">🎨</button>
|
|
110
|
+
<button class="process-btn">▶️</button>
|
|
111
|
+
<button class="download-btn secondary">💾</button>
|
|
112
|
+
<button class="download-pdf-btn secondary">📄</button>
|
|
113
|
+
<button class="print-btn secondary">🖨️</button>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="preview-container">
|
|
117
|
+
<div class="preview-placeholder">
|
|
118
|
+
<p>👈 Enter your Legal Markdown content and click Process to see the result</p>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</main>
|
|
123
|
+
|
|
124
|
+
<footer class="options-panel">
|
|
125
|
+
<button class="options-toggle">⚙️ Processing Options</button>
|
|
126
|
+
<div class="options-content">
|
|
127
|
+
<div class="options-grid">
|
|
128
|
+
<div class="option-group">
|
|
129
|
+
<label>Processing Control</label>
|
|
130
|
+
<div class="checkbox-group">
|
|
131
|
+
<label class="checkbox-item">
|
|
132
|
+
<input type="checkbox" id="debug"> Debug mode
|
|
133
|
+
<span class="tooltip">
|
|
134
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
135
|
+
<span class="tooltip-text">Shows detailed processing information in the console, including step-by-step parsing and variable substitution details.</span>
|
|
136
|
+
</span>
|
|
137
|
+
</label>
|
|
138
|
+
<label class="checkbox-item">
|
|
139
|
+
<input type="checkbox" id="yamlOnly"> YAML only
|
|
140
|
+
<span class="tooltip">
|
|
141
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
142
|
+
<span class="tooltip-text">Processes only the YAML front matter without rendering the markdown content. Useful for testing metadata extraction.</span>
|
|
143
|
+
</span>
|
|
144
|
+
</label>
|
|
145
|
+
<label class="checkbox-item">
|
|
146
|
+
<input type="checkbox" id="noHeaders"> No headers
|
|
147
|
+
<span class="tooltip">
|
|
148
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
149
|
+
<span class="tooltip-text">Disables automatic header numbering (l., ll., lll.) that's typically used in legal documents.</span>
|
|
150
|
+
</span>
|
|
151
|
+
</label>
|
|
152
|
+
<label class="checkbox-item">
|
|
153
|
+
<input type="checkbox" id="enableFieldTracking" checked> Field tracking
|
|
154
|
+
<span class="tooltip">
|
|
155
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
156
|
+
<span class="tooltip-text">Tracks all variable substitutions and provides a detailed report of which fields were processed and their values.</span>
|
|
157
|
+
</span>
|
|
158
|
+
</label>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="option-group">
|
|
163
|
+
<label>Advanced Options</label>
|
|
164
|
+
<div class="checkbox-group">
|
|
165
|
+
<label class="checkbox-item">
|
|
166
|
+
<input type="checkbox" id="noClauses"> No clauses
|
|
167
|
+
<span class="tooltip">
|
|
168
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
169
|
+
<span class="tooltip-text">Disables processing of structured clauses and conditional blocks (e.g., [text]{condition}). Content will be processed as plain text.</span>
|
|
170
|
+
</span>
|
|
171
|
+
</label>
|
|
172
|
+
<label class="checkbox-item">
|
|
173
|
+
<input type="checkbox" id="noReferences"> No references
|
|
174
|
+
<span class="tooltip">
|
|
175
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
176
|
+
<span class="tooltip-text">Disables processing of cross-references and citation links within the document. References will appear as plain text.</span>
|
|
177
|
+
</span>
|
|
178
|
+
</label>
|
|
179
|
+
<label class="checkbox-item">
|
|
180
|
+
<input type="checkbox" id="noMixins"> No mixins
|
|
181
|
+
<span class="tooltip">
|
|
182
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
183
|
+
<span class="tooltip-text">Disables processing of mixins and helper functions (e.g., {{formatDate()}}, {{capitalize()}}). Only basic variable substitution will work.</span>
|
|
184
|
+
</span>
|
|
185
|
+
</label>
|
|
186
|
+
<label class="checkbox-item">
|
|
187
|
+
<input type="checkbox" id="noImports"> No imports
|
|
188
|
+
<span class="tooltip">
|
|
189
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
190
|
+
<span class="tooltip-text">Disables importing of external markdown files. Import statements will be ignored and appear as plain text.</span>
|
|
191
|
+
</span>
|
|
192
|
+
</label>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="option-group">
|
|
197
|
+
<label>Header Options</label>
|
|
198
|
+
<div class="checkbox-group">
|
|
199
|
+
<label class="checkbox-item">
|
|
200
|
+
<input type="checkbox" id="noReset"> No reset
|
|
201
|
+
<span class="tooltip">
|
|
202
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
203
|
+
<span class="tooltip-text">Prevents resetting header counters. Header numbering will continue from previous values instead of starting fresh.</span>
|
|
204
|
+
</span>
|
|
205
|
+
</label>
|
|
206
|
+
<label class="checkbox-item">
|
|
207
|
+
<input type="checkbox" id="noIndent"> No indent
|
|
208
|
+
<span class="tooltip">
|
|
209
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
210
|
+
<span class="tooltip-text">Disables automatic indentation of nested headers and sub-clauses. All content will be left-aligned.</span>
|
|
211
|
+
</span>
|
|
212
|
+
</label>
|
|
213
|
+
<label class="checkbox-item">
|
|
214
|
+
<input type="checkbox" id="throwOnYamlError"> Throw on YAML error
|
|
215
|
+
<span class="tooltip">
|
|
216
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
217
|
+
<span class="tooltip-text">Stops processing and shows an error if YAML front matter is malformed. Otherwise, continues with partial parsing.</span>
|
|
218
|
+
</span>
|
|
219
|
+
</label>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div class="option-group">
|
|
224
|
+
<label>Export Options</label>
|
|
225
|
+
<div class="checkbox-group">
|
|
226
|
+
<label class="checkbox-item">
|
|
227
|
+
<input type="checkbox" id="exportYaml"> Export YAML
|
|
228
|
+
<span class="tooltip">
|
|
229
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
230
|
+
<span class="tooltip-text">Exports document metadata as a YAML file alongside the processed markdown. Useful for data exchange and backup.</span>
|
|
231
|
+
</span>
|
|
232
|
+
</label>
|
|
233
|
+
<label class="checkbox-item">
|
|
234
|
+
<input type="checkbox" id="exportJson"> Export JSON
|
|
235
|
+
<span class="tooltip">
|
|
236
|
+
<span class="tooltip-icon">ℹ️</span>
|
|
237
|
+
<span class="tooltip-text">Exports document metadata as a JSON file alongside the processed markdown. Useful for integration with other systems.</span>
|
|
238
|
+
</span>
|
|
239
|
+
</label>
|
|
240
|
+
</div>
|
|
241
|
+
<input type="text" id="title" placeholder="Document title (optional)">
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</footer>
|
|
246
|
+
|
|
247
|
+
<div id="messageArea"></div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<script src="./legal-markdown.umd.min.js"></script>
|
|
251
|
+
<script>
|
|
252
|
+
class LegalMarkdownApp {
|
|
253
|
+
constructor() {
|
|
254
|
+
this.processedResult = null;
|
|
255
|
+
this.metadataResult = null;
|
|
256
|
+
this.theme = localStorage.getItem('theme') || 'light';
|
|
257
|
+
this.customCSS = '';
|
|
258
|
+
this.baseStylesEnabled = true; // Track base styles state
|
|
259
|
+
this.initializeTheme();
|
|
260
|
+
this.initializeElements();
|
|
261
|
+
this.bindEvents();
|
|
262
|
+
this.updateBaseStyles(); // Initialize base styles
|
|
263
|
+
this.loadExample('service-agreement');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
initializeTheme() {
|
|
267
|
+
document.documentElement.setAttribute('data-theme', this.theme);
|
|
268
|
+
const themeToggle = document.querySelector('.theme-toggle');
|
|
269
|
+
if (themeToggle) {
|
|
270
|
+
themeToggle.textContent = this.theme === 'dark' ? '☀️' : '🌙';
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
initializeElements() {
|
|
275
|
+
this.editor = document.querySelector('.editor');
|
|
276
|
+
this.preview = document.querySelector('.preview-container');
|
|
277
|
+
this.processBtn = document.querySelector('.process-btn');
|
|
278
|
+
this.toggleStylesBtn = document.querySelector('.toggle-styles-btn');
|
|
279
|
+
this.downloadBtn = document.querySelector('.download-btn');
|
|
280
|
+
this.downloadPdfBtn = document.querySelector('.download-pdf-btn');
|
|
281
|
+
this.printBtn = document.querySelector('.print-btn');
|
|
282
|
+
this.themeToggle = document.querySelector('.theme-toggle');
|
|
283
|
+
this.optionsToggle = document.querySelector('.options-toggle');
|
|
284
|
+
this.optionsContent = document.querySelector('.options-content');
|
|
285
|
+
this.fileInput = document.querySelector('.file-input');
|
|
286
|
+
this.exampleSelect = document.querySelector('.example-select');
|
|
287
|
+
this.messageArea = document.getElementById('messageArea');
|
|
288
|
+
|
|
289
|
+
// CSS Editor elements
|
|
290
|
+
this.cssReset = document.querySelector('.css-reset');
|
|
291
|
+
this.cssContent = document.querySelector('.css-content');
|
|
292
|
+
this.cssEditor = document.querySelector('.css-editor');
|
|
293
|
+
this.cssFileInput = document.querySelector('.css-file-input');
|
|
294
|
+
this.cssExampleSelect = document.querySelector('.css-example-select');
|
|
295
|
+
|
|
296
|
+
// Column resizers
|
|
297
|
+
this.resizer1 = document.getElementById('resizer1');
|
|
298
|
+
this.resizer2 = document.getElementById('resizer2');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
bindEvents() {
|
|
302
|
+
this.processBtn.addEventListener('click', () => this.processContent());
|
|
303
|
+
this.toggleStylesBtn.addEventListener('click', () => this.toggleBaseStyles());
|
|
304
|
+
this.downloadBtn.addEventListener('click', () => this.downloadDocument());
|
|
305
|
+
this.downloadPdfBtn.addEventListener('click', () => this.downloadPDF());
|
|
306
|
+
this.printBtn.addEventListener('click', () => this.printDocument());
|
|
307
|
+
this.themeToggle.addEventListener('click', () => this.toggleTheme());
|
|
308
|
+
this.optionsToggle.addEventListener('click', () => this.toggleOptions());
|
|
309
|
+
this.fileInput.addEventListener('change', (e) => this.handleFileUpload(e));
|
|
310
|
+
this.exampleSelect.addEventListener('change', (e) => this.loadExample(e.target.value));
|
|
311
|
+
|
|
312
|
+
// CSS Editor events
|
|
313
|
+
this.cssReset.addEventListener('click', () => this.resetCSS());
|
|
314
|
+
this.cssFileInput.addEventListener('change', (e) => this.handleCSSFileUpload(e));
|
|
315
|
+
this.cssExampleSelect.addEventListener('change', (e) => this.loadCSSExample(e.target.value));
|
|
316
|
+
|
|
317
|
+
// Auto-apply CSS on input (debounced)
|
|
318
|
+
this.cssEditor.addEventListener('input',
|
|
319
|
+
this.debounce(() => this.applyCSS(), 500)
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Column resizing
|
|
323
|
+
this.initColumnResizing();
|
|
324
|
+
|
|
325
|
+
// Help button
|
|
326
|
+
const helpBtn = document.querySelector('.help-btn');
|
|
327
|
+
if (helpBtn) {
|
|
328
|
+
helpBtn.addEventListener('click', () => this.showHelp());
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Auto-process on input (debounced)
|
|
332
|
+
this.editor.addEventListener('input',
|
|
333
|
+
this.debounce(() => this.processContent(), 1000)
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Keyboard shortcuts
|
|
337
|
+
document.addEventListener('keydown', (e) => this.handleKeyboardShortcuts(e));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async processContent() {
|
|
341
|
+
const content = this.editor.value;
|
|
342
|
+
if (!content.trim()) {
|
|
343
|
+
this.showMessage('Please enter some content to process.', 'error');
|
|
344
|
+
this.clearPreview();
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Validate content structure
|
|
349
|
+
const validation = this.validateContent(content);
|
|
350
|
+
if (!validation.isValid) {
|
|
351
|
+
this.showMessage(validation.message, 'warning');
|
|
352
|
+
if (validation.severity === 'error') {
|
|
353
|
+
this.clearPreview();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
this.showProcessing();
|
|
360
|
+
|
|
361
|
+
const options = this.getProcessingOptions();
|
|
362
|
+
const result = window.LegalMarkdown.processLegalMarkdown(content, options);
|
|
363
|
+
|
|
364
|
+
this.processedResult = result.content;
|
|
365
|
+
this.metadataResult = result.metadata;
|
|
366
|
+
|
|
367
|
+
this.displayResult(result);
|
|
368
|
+
this.showProcessingStats(result);
|
|
369
|
+
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error('Processing error:', error);
|
|
372
|
+
this.handleProcessingError(error);
|
|
373
|
+
} finally {
|
|
374
|
+
this.hideProcessing();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
getProcessingOptions() {
|
|
379
|
+
return {
|
|
380
|
+
debug: document.getElementById('debug').checked,
|
|
381
|
+
yamlOnly: document.getElementById('yamlOnly').checked,
|
|
382
|
+
noHeaders: document.getElementById('noHeaders').checked,
|
|
383
|
+
noClauses: document.getElementById('noClauses').checked,
|
|
384
|
+
noReferences: document.getElementById('noReferences').checked,
|
|
385
|
+
noMixins: document.getElementById('noMixins').checked,
|
|
386
|
+
noImports: document.getElementById('noImports').checked,
|
|
387
|
+
noReset: document.getElementById('noReset').checked,
|
|
388
|
+
noIndent: document.getElementById('noIndent').checked,
|
|
389
|
+
throwOnYamlError: document.getElementById('throwOnYamlError').checked,
|
|
390
|
+
enableFieldTracking: document.getElementById('enableFieldTracking').checked,
|
|
391
|
+
exportMetadata: document.getElementById('exportYaml').checked || document.getElementById('exportJson').checked,
|
|
392
|
+
exportFormat: document.getElementById('exportYaml').checked ? 'yaml' : 'json'
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
displayResult(result) {
|
|
397
|
+
this.preview.innerHTML = `
|
|
398
|
+
<div class="legal-document">
|
|
399
|
+
${this.formatMarkdownForDisplay(result.content)}
|
|
400
|
+
</div>
|
|
401
|
+
`;
|
|
402
|
+
|
|
403
|
+
// Apply base styles if enabled
|
|
404
|
+
this.updateBaseStyles();
|
|
405
|
+
// Apply custom CSS if present
|
|
406
|
+
this.updatePreviewWithCSS();
|
|
407
|
+
|
|
408
|
+
if (result.fieldReport) {
|
|
409
|
+
console.log('Field Report:', result.fieldReport);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
formatMarkdownForDisplay(content) {
|
|
414
|
+
// Basic markdown to HTML conversion for display
|
|
415
|
+
return content
|
|
416
|
+
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
|
|
417
|
+
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
|
|
418
|
+
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
|
|
419
|
+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
420
|
+
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
421
|
+
.replace(/\n\n/g, '</p><p>')
|
|
422
|
+
.replace(/^(.+)$/gm, '<p>$1</p>')
|
|
423
|
+
.replace(/<p><\/p>/g, '')
|
|
424
|
+
.replace(/<p>(<h[1-6]>.*<\/h[1-6]>)<\/p>/g, '$1');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
downloadDocument() {
|
|
428
|
+
if (!this.processedResult) {
|
|
429
|
+
this.showMessage('Please process a document first.', 'error');
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const title = document.getElementById('title').value || 'legal-document';
|
|
434
|
+
const filename = `${title.replace(/[^a-zA-Z0-9]/g, '-')}.md`;
|
|
435
|
+
this.downloadFile(this.processedResult, filename);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
printDocument() {
|
|
439
|
+
if (!this.processedResult) {
|
|
440
|
+
this.showMessage('Please process a document first.', 'error');
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const printContent = this.preview.innerHTML;
|
|
445
|
+
const title = document.getElementById('title').value || 'Legal Document';
|
|
446
|
+
|
|
447
|
+
const printWindow = window.open('', '_blank');
|
|
448
|
+
printWindow.document.write(`
|
|
449
|
+
<!DOCTYPE html>
|
|
450
|
+
<html>
|
|
451
|
+
<head>
|
|
452
|
+
<title>${title}</title>
|
|
453
|
+
<style>
|
|
454
|
+
${this.getContractCSS()}
|
|
455
|
+
</style>
|
|
456
|
+
</head>
|
|
457
|
+
<body onload="window.print(); window.close();">
|
|
458
|
+
${printContent}
|
|
459
|
+
</body>
|
|
460
|
+
</html>
|
|
461
|
+
`);
|
|
462
|
+
printWindow.document.close();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async downloadPDF() {
|
|
466
|
+
if (!this.processedResult) {
|
|
467
|
+
this.showMessage('Please process a document first.', 'error');
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const title = document.getElementById('title').value || 'legal-document';
|
|
472
|
+
const printContent = this.preview.innerHTML;
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
// Show loading message
|
|
476
|
+
this.showMessage('Generating PDF... Please wait.', 'info');
|
|
477
|
+
|
|
478
|
+
// Create a hidden iframe for PDF generation
|
|
479
|
+
const iframe = document.createElement('iframe');
|
|
480
|
+
iframe.style.display = 'none';
|
|
481
|
+
document.body.appendChild(iframe);
|
|
482
|
+
|
|
483
|
+
iframe.contentDocument.open();
|
|
484
|
+
iframe.contentDocument.write(`
|
|
485
|
+
<!DOCTYPE html>
|
|
486
|
+
<html>
|
|
487
|
+
<head>
|
|
488
|
+
<title>${title}</title>
|
|
489
|
+
<style>
|
|
490
|
+
${this.getContractCSS()}
|
|
491
|
+
@media print {
|
|
492
|
+
body { margin: 0; }
|
|
493
|
+
@page { margin: 15mm; }
|
|
494
|
+
}
|
|
495
|
+
</style>
|
|
496
|
+
</head>
|
|
497
|
+
<body>
|
|
498
|
+
${printContent}
|
|
499
|
+
</body>
|
|
500
|
+
</html>
|
|
501
|
+
`);
|
|
502
|
+
iframe.contentDocument.close();
|
|
503
|
+
|
|
504
|
+
// Wait for content to load
|
|
505
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
506
|
+
|
|
507
|
+
// Focus the iframe and trigger print
|
|
508
|
+
iframe.contentWindow.focus();
|
|
509
|
+
iframe.contentWindow.print();
|
|
510
|
+
|
|
511
|
+
// Clean up
|
|
512
|
+
setTimeout(() => {
|
|
513
|
+
document.body.removeChild(iframe);
|
|
514
|
+
this.showMessage('PDF generation initiated. Use your browser\'s print dialog to save as PDF.', 'success');
|
|
515
|
+
}, 1000);
|
|
516
|
+
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error('PDF generation error:', error);
|
|
519
|
+
this.showMessage('PDF generation failed. Please use the Print button instead.', 'error');
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
getContractCSS() {
|
|
524
|
+
const baseCSS = `
|
|
525
|
+
body {
|
|
526
|
+
font-family: Helvetica, Arial, sans-serif;
|
|
527
|
+
font-size: 12pt;
|
|
528
|
+
line-height: 1.3;
|
|
529
|
+
color: #333;
|
|
530
|
+
max-width: 210mm;
|
|
531
|
+
margin: 0 auto;
|
|
532
|
+
padding: 20mm 15mm;
|
|
533
|
+
background-color: white;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
h1 {
|
|
537
|
+
color: #353954;
|
|
538
|
+
font-size: 1.6em;
|
|
539
|
+
line-height: 1.5em;
|
|
540
|
+
margin: 8px 8px 32px;
|
|
541
|
+
text-align: center;
|
|
542
|
+
border-bottom: solid #02370a;
|
|
543
|
+
padding: 0 5% 0.3em;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
h2 {
|
|
547
|
+
font-size: 14pt;
|
|
548
|
+
margin-top: 20px;
|
|
549
|
+
margin-bottom: 15px;
|
|
550
|
+
border-bottom: 1px solid #41badf;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
h3 {
|
|
554
|
+
font-size: 10pt;
|
|
555
|
+
font-weight: bold;
|
|
556
|
+
border-bottom: 1px solid #41badf;
|
|
557
|
+
margin-top: 30px;
|
|
558
|
+
margin-bottom: 8px;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
p {
|
|
562
|
+
margin-bottom: 15px;
|
|
563
|
+
text-align: justify;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
@media print {
|
|
567
|
+
@page {
|
|
568
|
+
size: A4;
|
|
569
|
+
margin: 20mm 10mm 10mm 10mm;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
body {
|
|
573
|
+
font-size: 11pt;
|
|
574
|
+
color: black;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
`;
|
|
578
|
+
|
|
579
|
+
// Add custom CSS if present
|
|
580
|
+
if (this.customCSS.trim()) {
|
|
581
|
+
return baseCSS + '\n\n/* Custom CSS */\n' + this.customCSS;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return baseCSS;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
toggleTheme() {
|
|
588
|
+
this.theme = this.theme === 'light' ? 'dark' : 'light';
|
|
589
|
+
localStorage.setItem('theme', this.theme);
|
|
590
|
+
this.initializeTheme();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
toggleOptions() {
|
|
594
|
+
this.optionsContent.classList.toggle('open');
|
|
595
|
+
this.optionsToggle.textContent = this.optionsContent.classList.contains('open') ?
|
|
596
|
+
'🔽 Processing Options' :
|
|
597
|
+
'⚙️ Processing Options';
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
handleFileUpload(event) {
|
|
601
|
+
const file = event.target.files[0];
|
|
602
|
+
if (!file) return;
|
|
603
|
+
|
|
604
|
+
// Validate file type
|
|
605
|
+
const allowedTypes = ['text/plain', 'text/markdown', ''];
|
|
606
|
+
const allowedExtensions = ['.md', '.txt', '.markdown'];
|
|
607
|
+
|
|
608
|
+
const fileExtension = file.name.toLowerCase().substr(file.name.lastIndexOf('.'));
|
|
609
|
+
if (!allowedTypes.includes(file.type) && !allowedExtensions.includes(fileExtension)) {
|
|
610
|
+
this.showMessage('Please select a valid text file (.md, .txt, .markdown)', 'error');
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Validate file size (max 1MB)
|
|
615
|
+
const maxSize = 1024 * 1024; // 1MB
|
|
616
|
+
if (file.size > maxSize) {
|
|
617
|
+
this.showMessage('File too large. Maximum size is 1MB.', 'error');
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const reader = new FileReader();
|
|
622
|
+
reader.onload = (e) => {
|
|
623
|
+
try {
|
|
624
|
+
const content = e.target.result;
|
|
625
|
+
this.editor.value = content;
|
|
626
|
+
this.exampleSelect.value = ''; // Clear example selection
|
|
627
|
+
this.processContent();
|
|
628
|
+
this.showMessage(`📁 File "${file.name}" loaded successfully`, 'success');
|
|
629
|
+
} catch (error) {
|
|
630
|
+
this.showMessage('Error reading file: ' + error.message, 'error');
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
reader.onerror = () => {
|
|
635
|
+
this.showMessage('Error reading file. Please try again.', 'error');
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
reader.readAsText(file);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
getExamples() {
|
|
642
|
+
return {
|
|
643
|
+
'service-agreement': {
|
|
644
|
+
title: 'Service Agreement',
|
|
645
|
+
content: `---
|
|
646
|
+
title: Service Agreement
|
|
647
|
+
client: Acme Corporation
|
|
648
|
+
provider: Professional Services LLC
|
|
649
|
+
effective_date: 2024-01-01
|
|
650
|
+
payment_terms: 30
|
|
651
|
+
jurisdiction: California
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
# {{title}}
|
|
655
|
+
|
|
656
|
+
This {{title}} is entered into on {{effective_date}} between:
|
|
657
|
+
|
|
658
|
+
**CLIENT**: {{client}}
|
|
659
|
+
**PROVIDER**: {{provider}}
|
|
660
|
+
|
|
661
|
+
l. Terms and Conditions
|
|
662
|
+
ll. Services
|
|
663
|
+
The Provider agrees to perform professional services as detailed in the attached statement of work.
|
|
664
|
+
|
|
665
|
+
ll. Payment Terms
|
|
666
|
+
Payment is due within {{payment_terms}} days of invoice date.
|
|
667
|
+
|
|
668
|
+
ll. Jurisdiction
|
|
669
|
+
This Agreement shall be governed by the laws of {{jurisdiction}}.
|
|
670
|
+
|
|
671
|
+
l. Conclusion
|
|
672
|
+
This concludes the {{title}} between the parties.`
|
|
673
|
+
},
|
|
674
|
+
|
|
675
|
+
'lease-contract': {
|
|
676
|
+
title: 'Office Lease Agreement',
|
|
677
|
+
content: `---
|
|
678
|
+
title: OFFICE SPACE LEASE AGREEMENT
|
|
679
|
+
contract:
|
|
680
|
+
signing_date: 2024-01-15
|
|
681
|
+
signing_city: San Francisco
|
|
682
|
+
lessor:
|
|
683
|
+
company_name: Property Management LLC
|
|
684
|
+
registered_address: 123 Main Street, San Francisco, CA 94105
|
|
685
|
+
tax_id: 12-3456789
|
|
686
|
+
representative:
|
|
687
|
+
full_name: John Smith
|
|
688
|
+
id_number: 123456789
|
|
689
|
+
lessee:
|
|
690
|
+
company_name: Tech Startup Inc
|
|
691
|
+
registered_address: 456 Innovation Way, San Francisco, CA 94107
|
|
692
|
+
tax_id: 98-7654321
|
|
693
|
+
representative:
|
|
694
|
+
full_name: Jane Doe
|
|
695
|
+
id_number: ""
|
|
696
|
+
property:
|
|
697
|
+
address: 789 Business Plaza, Suite 500, San Francisco, CA 94108
|
|
698
|
+
area_sqm: 250
|
|
699
|
+
parking_spots: 5
|
|
700
|
+
payment:
|
|
701
|
+
monthly_rent:
|
|
702
|
+
text: Five Thousand
|
|
703
|
+
number: 5000
|
|
704
|
+
late_fee_applies: true
|
|
705
|
+
late_fee_percentage: 1.5
|
|
706
|
+
maintenance_included: true
|
|
707
|
+
maintenance:
|
|
708
|
+
included_services:
|
|
709
|
+
- Daily cleaning
|
|
710
|
+
- HVAC maintenance
|
|
711
|
+
- Security services
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
# {{title}}
|
|
715
|
+
|
|
716
|
+
This agreement is entered into on {{contract.signing_date}} in {{contract.signing_city}}.
|
|
717
|
+
|
|
718
|
+
## Parties
|
|
719
|
+
|
|
720
|
+
Between:
|
|
721
|
+
|
|
722
|
+
**{{lessor.company_name}}**, with registered office at {{lessor.registered_address}}, and Tax ID {{lessor.tax_id}}, hereinafter referred to as "THE LESSOR".
|
|
723
|
+
|
|
724
|
+
Represented by {{lessor.representative.full_name}} with ID {{lessor.representative.id_number}}.
|
|
725
|
+
|
|
726
|
+
And:
|
|
727
|
+
|
|
728
|
+
**{{lessee.company_name}}**, with registered office at {{lessee.registered_address}}, and Tax ID {{lessee.tax_id}}, hereinafter referred to as "THE LESSEE".
|
|
729
|
+
|
|
730
|
+
Represented by {{lessee.representative.full_name}} with ID {{lessee.representative.id_number ? lessee.representative.id_number : "[ID Required]"}}.
|
|
731
|
+
|
|
732
|
+
## Property Details
|
|
733
|
+
|
|
734
|
+
- **Address**: {{property.address}}
|
|
735
|
+
- **Area**: {{property.area_sqm}} square meters
|
|
736
|
+
- **Parking Spots**: {{property.parking_spots}}
|
|
737
|
+
- **Monthly Rent**: {{payment.monthly_rent.text}} ({{payment.monthly_rent.number}}) USD
|
|
738
|
+
|
|
739
|
+
[## Late Payment Clause
|
|
740
|
+
|
|
741
|
+
Late payments will incur a fee of {{payment.late_fee_percentage}}% per month.]{payment.late_fee_applies}
|
|
742
|
+
|
|
743
|
+
[## Maintenance
|
|
744
|
+
|
|
745
|
+
The following maintenance services are included:
|
|
746
|
+
{{#maintenance_included}}
|
|
747
|
+
{{#each maintenance.included_services}}
|
|
748
|
+
- {{this}}
|
|
749
|
+
{{/each}}
|
|
750
|
+
{{/maintenance_included}}]{maintenance_included}`
|
|
751
|
+
},
|
|
752
|
+
|
|
753
|
+
'purchase-ticket': {
|
|
754
|
+
title: 'Purchase Ticket',
|
|
755
|
+
content: '---\n' +
|
|
756
|
+
'storeName: TechMart Store\n' +
|
|
757
|
+
'cashierName: Alice Johnson\n' +
|
|
758
|
+
'ticketNumber: "12345"\n' +
|
|
759
|
+
'receiptId: RCP-2024-12345\n' +
|
|
760
|
+
'taxRate: 8.5\n' +
|
|
761
|
+
'items:\n' +
|
|
762
|
+
' - name: Laptop Computer\n' +
|
|
763
|
+
' price: 999.99\n' +
|
|
764
|
+
' onSale: false\n' +
|
|
765
|
+
' - name: Wireless Mouse\n' +
|
|
766
|
+
' price: 29.99\n' +
|
|
767
|
+
' onSale: true\n' +
|
|
768
|
+
' - name: USB Cable\n' +
|
|
769
|
+
' price: null\n' +
|
|
770
|
+
'subtotal: 1029.98\n' +
|
|
771
|
+
'taxAmount: 87.55\n' +
|
|
772
|
+
'total: 1117.53\n' +
|
|
773
|
+
'loyaltyMember: true\n' +
|
|
774
|
+
'pointsEarned: 112\n' +
|
|
775
|
+
'pointsBalance: 1523\n' +
|
|
776
|
+
'---\n' +
|
|
777
|
+
'\n' +
|
|
778
|
+
'# Purchase Ticket - {{ticketNumber}}\n' +
|
|
779
|
+
'\n' +
|
|
780
|
+
'**Date:** @today **Store:** {{storeName}}\n' +
|
|
781
|
+
'**Cashier:** {{cashierName}}\n' +
|
|
782
|
+
'\n' +
|
|
783
|
+
'## Purchased Items\n' +
|
|
784
|
+
'\n' +
|
|
785
|
+
'{{#items}}\n' +
|
|
786
|
+
'- {{name}} {{price ? "$" + price : "[Price Missing]"}} {{onSale ? "(ON SALE!)" : ""}}\n' +
|
|
787
|
+
'{{/items}}\n' +
|
|
788
|
+
'\n' +
|
|
789
|
+
'## Payment Details\n' +
|
|
790
|
+
'\n' +
|
|
791
|
+
'- Subtotal: ${{subtotal}}\n' +
|
|
792
|
+
'- Tax ({{taxRate}}%): ${{taxAmount}}\n' +
|
|
793
|
+
'- **Total**: ${{total}}\n' +
|
|
794
|
+
'\n' +
|
|
795
|
+
'{{#loyaltyMember}}\n' +
|
|
796
|
+
'## Customer Loyalty\n' +
|
|
797
|
+
'\n' +
|
|
798
|
+
'- Member Points Earned: {{pointsEarned}}\n' +
|
|
799
|
+
'- Current Balance: {{pointsBalance}}\n' +
|
|
800
|
+
'{{/loyaltyMember}}\n' +
|
|
801
|
+
'\n' +
|
|
802
|
+
'---\n' +
|
|
803
|
+
'\n' +
|
|
804
|
+
'Thank you for shopping with us!\n' +
|
|
805
|
+
'Receipt ID: {{receiptId}}'
|
|
806
|
+
},
|
|
807
|
+
|
|
808
|
+
'nda': {
|
|
809
|
+
title: 'Non-Disclosure Agreement',
|
|
810
|
+
content: `---
|
|
811
|
+
title: NON-DISCLOSURE AGREEMENT
|
|
812
|
+
disclosing_party: TechCorp Inc.
|
|
813
|
+
receiving_party: John Developer
|
|
814
|
+
effective_date: 2024-01-01
|
|
815
|
+
duration_months: 24
|
|
816
|
+
jurisdiction: Delaware
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
# {{title}}
|
|
820
|
+
|
|
821
|
+
This {{title}} is entered into on {{effective_date}} between:
|
|
822
|
+
|
|
823
|
+
**DISCLOSING PARTY**: {{disclosing_party}}
|
|
824
|
+
**RECEIVING PARTY**: {{receiving_party}}
|
|
825
|
+
|
|
826
|
+
l. Definition of Confidential Information
|
|
827
|
+
ll. Confidential Information includes all technical data, trade secrets, know-how, research, product plans, products, services, customers, customer lists, markets, software, developments, inventions, processes, formulas, technology, designs, drawings, engineering, hardware configuration information, marketing, finances, or other business information.
|
|
828
|
+
|
|
829
|
+
l. Obligations of Receiving Party
|
|
830
|
+
ll. Protection of Confidential Information
|
|
831
|
+
The Receiving Party agrees to hold and maintain the Confidential Information in strict confidence.
|
|
832
|
+
|
|
833
|
+
ll. Non-Disclosure
|
|
834
|
+
The Receiving Party agrees not to disclose any Confidential Information to third parties without prior written consent.
|
|
835
|
+
|
|
836
|
+
ll. Use Restriction
|
|
837
|
+
The Receiving Party agrees to use the Confidential Information solely for the purpose of evaluating potential business opportunities.
|
|
838
|
+
|
|
839
|
+
l. Term
|
|
840
|
+
This Agreement shall remain in effect for {{duration_months}} months from the effective date.
|
|
841
|
+
|
|
842
|
+
l. Governing Law
|
|
843
|
+
This Agreement shall be governed by the laws of {{jurisdiction}}.
|
|
844
|
+
|
|
845
|
+
l. Signatures
|
|
846
|
+
By signing below, both parties agree to the terms and conditions of this Agreement.
|
|
847
|
+
|
|
848
|
+
**{{disclosing_party}}**
|
|
849
|
+
|
|
850
|
+
Signature: ___________________________ Date: ___________
|
|
851
|
+
|
|
852
|
+
**{{receiving_party}}**
|
|
853
|
+
|
|
854
|
+
Signature: ___________________________ Date: ___________`
|
|
855
|
+
},
|
|
856
|
+
|
|
857
|
+
'employment-contract': {
|
|
858
|
+
title: 'Employment Contract',
|
|
859
|
+
content: '---\n' +
|
|
860
|
+
'title: EMPLOYMENT AGREEMENT\n' +
|
|
861
|
+
'company: TechCorp Inc.\n' +
|
|
862
|
+
'employee: Jane Smith\n' +
|
|
863
|
+
'position: Senior Software Engineer\n' +
|
|
864
|
+
'start_date: 2024-02-01\n' +
|
|
865
|
+
'salary: 95000\n' +
|
|
866
|
+
'benefits_package: Standard\n' +
|
|
867
|
+
'probation_period: 3\n' +
|
|
868
|
+
'vacation_days: 20\n' +
|
|
869
|
+
'---\n' +
|
|
870
|
+
'\n' +
|
|
871
|
+
'# {{title}}\n' +
|
|
872
|
+
'\n' +
|
|
873
|
+
'This {{title}} is entered into between:\n' +
|
|
874
|
+
'\n' +
|
|
875
|
+
'**COMPANY**: {{company}}\n' +
|
|
876
|
+
'**EMPLOYEE**: {{employee}}\n' +
|
|
877
|
+
'\n' +
|
|
878
|
+
'l. Position and Duties\n' +
|
|
879
|
+
'll. Position\n' +
|
|
880
|
+
'The Employee is hereby employed as {{position}}.\n' +
|
|
881
|
+
'\n' +
|
|
882
|
+
'll. Start Date\n' +
|
|
883
|
+
'Employment shall commence on {{start_date}}.\n' +
|
|
884
|
+
'\n' +
|
|
885
|
+
'll. Probationary Period\n' +
|
|
886
|
+
'The Employee shall serve a probationary period of {{probation_period}} months.\n' +
|
|
887
|
+
'\n' +
|
|
888
|
+
'l. Compensation and Benefits\n' +
|
|
889
|
+
'll. Salary\n' +
|
|
890
|
+
'The Employee shall receive an annual salary of ${{salary}}.\n' +
|
|
891
|
+
'\n' +
|
|
892
|
+
'll. Benefits\n' +
|
|
893
|
+
'The Employee shall be entitled to the {{benefits_package}} benefits package.\n' +
|
|
894
|
+
'\n' +
|
|
895
|
+
'll. Vacation\n' +
|
|
896
|
+
'The Employee shall be entitled to {{vacation_days}} vacation days per year.\n' +
|
|
897
|
+
'\n' +
|
|
898
|
+
'l. Termination\n' +
|
|
899
|
+
'll. Either party may terminate this agreement with 30 days written notice.\n' +
|
|
900
|
+
'\n' +
|
|
901
|
+
'l. Confidentiality\n' +
|
|
902
|
+
'll. The Employee agrees to maintain the confidentiality of all company information.\n' +
|
|
903
|
+
'\n' +
|
|
904
|
+
'l. Governing Law\n' +
|
|
905
|
+
'This Agreement shall be governed by the laws of the state in which the Company is located.\n' +
|
|
906
|
+
'\n' +
|
|
907
|
+
'**Signatures**\n' +
|
|
908
|
+
'\n' +
|
|
909
|
+
'Company Representative: ___________________________ Date: ___________\n' +
|
|
910
|
+
'\n' +
|
|
911
|
+
'Employee: ___________________________ Date: ___________'
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
loadExample(exampleKey) {
|
|
917
|
+
if (!exampleKey) return;
|
|
918
|
+
|
|
919
|
+
const examples = this.getExamples();
|
|
920
|
+
const example = examples[exampleKey];
|
|
921
|
+
|
|
922
|
+
if (example) {
|
|
923
|
+
this.editor.value = example.content;
|
|
924
|
+
this.exampleSelect.value = exampleKey;
|
|
925
|
+
this.processContent();
|
|
926
|
+
} else {
|
|
927
|
+
this.showMessage('Example not found.', 'error');
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
validateContent(content) {
|
|
932
|
+
const validation = {
|
|
933
|
+
isValid: true,
|
|
934
|
+
message: '',
|
|
935
|
+
severity: 'info'
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
// Check for YAML front matter
|
|
939
|
+
const hasYamlFrontMatter = content.trim().startsWith('---');
|
|
940
|
+
if (!hasYamlFrontMatter) {
|
|
941
|
+
validation.isValid = false;
|
|
942
|
+
validation.message = 'No YAML front matter detected. Consider adding document metadata with ---';
|
|
943
|
+
validation.severity = 'warning';
|
|
944
|
+
return validation;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Check for balanced YAML front matter
|
|
948
|
+
const yamlMatches = content.match(/^---\s*\n([\s\S]*?)\n---\s*$/m);
|
|
949
|
+
if (!yamlMatches) {
|
|
950
|
+
validation.isValid = false;
|
|
951
|
+
validation.message = 'YAML front matter is not properly formatted. Ensure it starts and ends with ---';
|
|
952
|
+
validation.severity = 'error';
|
|
953
|
+
return validation;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Check for common YAML errors
|
|
957
|
+
const yamlContent = yamlMatches[1];
|
|
958
|
+
if (yamlContent.includes('\t')) {
|
|
959
|
+
validation.isValid = false;
|
|
960
|
+
validation.message = 'YAML contains tabs. Use spaces for indentation instead.';
|
|
961
|
+
validation.severity = 'error';
|
|
962
|
+
return validation;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// Check for template variables
|
|
966
|
+
const templateVars = content.match(/\{\{([^}]+)\}\}/g);
|
|
967
|
+
if (templateVars) {
|
|
968
|
+
const uniqueVars = [...new Set(templateVars.map(v => v.replace(/[{}]/g, '').trim()))];
|
|
969
|
+
if (uniqueVars.length > 10) {
|
|
970
|
+
validation.isValid = false;
|
|
971
|
+
validation.message = `Document contains ${uniqueVars.length} template variables. Consider breaking into sections.`;
|
|
972
|
+
validation.severity = 'warning';
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// Check for unbalanced brackets
|
|
977
|
+
const openBrackets = (content.match(/\{\{/g) || []).length;
|
|
978
|
+
const closeBrackets = (content.match(/\}\}/g) || []).length;
|
|
979
|
+
if (openBrackets !== closeBrackets) {
|
|
980
|
+
validation.isValid = false;
|
|
981
|
+
validation.message = `Unbalanced template brackets: ${openBrackets} opening, ${closeBrackets} closing`;
|
|
982
|
+
validation.severity = 'error';
|
|
983
|
+
return validation;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return validation;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
handleProcessingError(error) {
|
|
990
|
+
let userMessage = 'Error processing document: ';
|
|
991
|
+
let suggestions = [];
|
|
992
|
+
|
|
993
|
+
if (error.message.includes('YAML')) {
|
|
994
|
+
userMessage += 'YAML parsing error. ';
|
|
995
|
+
suggestions.push('Check your YAML syntax in the front matter');
|
|
996
|
+
suggestions.push('Ensure proper indentation with spaces, not tabs');
|
|
997
|
+
suggestions.push('Verify all YAML keys have values');
|
|
998
|
+
} else if (error.message.includes('template')) {
|
|
999
|
+
userMessage += 'Template processing error. ';
|
|
1000
|
+
suggestions.push('Check that all variables are defined in YAML front matter');
|
|
1001
|
+
suggestions.push('Verify template syntax: {{variable}}');
|
|
1002
|
+
} else if (error.message.includes('reference')) {
|
|
1003
|
+
userMessage += 'Reference processing error. ';
|
|
1004
|
+
suggestions.push('Check reference syntax and file paths');
|
|
1005
|
+
} else {
|
|
1006
|
+
userMessage += error.message;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if (suggestions.length > 0) {
|
|
1010
|
+
userMessage += '\n\nSuggestions:\n• ' + suggestions.join('\n• ');
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
this.showMessage(userMessage, 'error');
|
|
1014
|
+
this.clearPreview();
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
showProcessingStats(result) {
|
|
1018
|
+
let stats = [];
|
|
1019
|
+
|
|
1020
|
+
if (result.metadata) {
|
|
1021
|
+
const yamlKeys = Object.keys(result.metadata).length;
|
|
1022
|
+
stats.push(`${yamlKeys} YAML variables`);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (result.fieldReport) {
|
|
1026
|
+
stats.push(`${result.fieldReport.length} field substitutions`);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const wordCount = result.content.split(/\s+/).filter(word => word.length > 0).length;
|
|
1030
|
+
stats.push(`${wordCount} words`);
|
|
1031
|
+
|
|
1032
|
+
if (stats.length > 0) {
|
|
1033
|
+
this.showMessage(`✅ Processing complete: ${stats.join(', ')}`, 'success');
|
|
1034
|
+
} else {
|
|
1035
|
+
this.showMessage('Document processed successfully!', 'success');
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
clearPreview() {
|
|
1040
|
+
this.preview.innerHTML = `
|
|
1041
|
+
<div class="preview-placeholder">
|
|
1042
|
+
<p>👈 Enter your Legal Markdown content and click Process to see the result</p>
|
|
1043
|
+
</div>
|
|
1044
|
+
`;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
showHelp() {
|
|
1048
|
+
const helpContent = `📖 Legal Markdown Help
|
|
1049
|
+
|
|
1050
|
+
🚀 Quick Start:
|
|
1051
|
+
• Select an example from the dropdown to get started
|
|
1052
|
+
• Or write your own content with YAML front matter
|
|
1053
|
+
• Use {{variable}} syntax for template substitutions
|
|
1054
|
+
|
|
1055
|
+
📝 Document Structure:
|
|
1056
|
+
---
|
|
1057
|
+
title: Your Document Title
|
|
1058
|
+
variable: value
|
|
1059
|
+
---
|
|
1060
|
+
|
|
1061
|
+
# {{title}}
|
|
1062
|
+
|
|
1063
|
+
Your content with {{variable}} substitutions...
|
|
1064
|
+
|
|
1065
|
+
⌨️ Keyboard Shortcuts:
|
|
1066
|
+
• Ctrl+Enter: Process document
|
|
1067
|
+
• Ctrl+P: Print document
|
|
1068
|
+
• Ctrl+S: Download Markdown
|
|
1069
|
+
• Ctrl+D: Download PDF
|
|
1070
|
+
|
|
1071
|
+
🔧 Processing Options:
|
|
1072
|
+
• Field Tracking: Highlight variable substitutions
|
|
1073
|
+
• Debug Mode: Show detailed processing information
|
|
1074
|
+
• Export Options: Save metadata as YAML or JSON
|
|
1075
|
+
|
|
1076
|
+
💡 Tips:
|
|
1077
|
+
• Use proper YAML indentation (spaces, not tabs)
|
|
1078
|
+
• Check template brackets are balanced {{}}
|
|
1079
|
+
• Use legal numbering: l., ll., lll. for sections`;
|
|
1080
|
+
|
|
1081
|
+
this.showMessage(helpContent, 'info');
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
handleKeyboardShortcuts(event) {
|
|
1085
|
+
if (event.ctrlKey || event.metaKey) {
|
|
1086
|
+
switch (event.key) {
|
|
1087
|
+
case 'Enter':
|
|
1088
|
+
event.preventDefault();
|
|
1089
|
+
this.processContent();
|
|
1090
|
+
break;
|
|
1091
|
+
case 'p':
|
|
1092
|
+
event.preventDefault();
|
|
1093
|
+
this.printDocument();
|
|
1094
|
+
break;
|
|
1095
|
+
case 's':
|
|
1096
|
+
event.preventDefault();
|
|
1097
|
+
this.downloadDocument();
|
|
1098
|
+
break;
|
|
1099
|
+
case 'd':
|
|
1100
|
+
event.preventDefault();
|
|
1101
|
+
this.downloadPDF();
|
|
1102
|
+
break;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
showProcessing() {
|
|
1108
|
+
this.processBtn.disabled = true;
|
|
1109
|
+
this.processBtn.textContent = '⏳ Processing...';
|
|
1110
|
+
this.preview.classList.add('loading');
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
hideProcessing() {
|
|
1114
|
+
this.processBtn.disabled = false;
|
|
1115
|
+
this.processBtn.textContent = '▶️ Process';
|
|
1116
|
+
this.preview.classList.remove('loading');
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
showMessage(message, type) {
|
|
1120
|
+
const messageDiv = document.createElement('div');
|
|
1121
|
+
messageDiv.className = `message ${type}`;
|
|
1122
|
+
|
|
1123
|
+
// Handle multi-line messages
|
|
1124
|
+
if (message.includes('\n')) {
|
|
1125
|
+
const lines = message.split('\n');
|
|
1126
|
+
const mainMessage = lines[0];
|
|
1127
|
+
const details = lines.slice(1).join('\n');
|
|
1128
|
+
|
|
1129
|
+
messageDiv.innerHTML = `
|
|
1130
|
+
<div class="message-main">${this.escapeHtml(mainMessage)}</div>
|
|
1131
|
+
<div class="message-details">${this.escapeHtml(details)}</div>
|
|
1132
|
+
`;
|
|
1133
|
+
} else {
|
|
1134
|
+
messageDiv.textContent = message;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
this.messageArea.innerHTML = '';
|
|
1138
|
+
this.messageArea.appendChild(messageDiv);
|
|
1139
|
+
|
|
1140
|
+
// Auto-hide success messages, keep errors/warnings visible longer
|
|
1141
|
+
const hideDelay = type === 'success' ? 3000 : 8000;
|
|
1142
|
+
setTimeout(() => {
|
|
1143
|
+
if (this.messageArea.contains(messageDiv)) {
|
|
1144
|
+
messageDiv.style.opacity = '0';
|
|
1145
|
+
setTimeout(() => {
|
|
1146
|
+
if (this.messageArea.contains(messageDiv)) {
|
|
1147
|
+
this.messageArea.removeChild(messageDiv);
|
|
1148
|
+
}
|
|
1149
|
+
}, 300);
|
|
1150
|
+
}
|
|
1151
|
+
}, hideDelay);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
escapeHtml(text) {
|
|
1155
|
+
const div = document.createElement('div');
|
|
1156
|
+
div.textContent = text;
|
|
1157
|
+
return div.innerHTML;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
downloadFile(content, filename) {
|
|
1161
|
+
const blob = new Blob([content], {
|
|
1162
|
+
type: 'text/plain'
|
|
1163
|
+
});
|
|
1164
|
+
const url = URL.createObjectURL(blob);
|
|
1165
|
+
const a = document.createElement('a');
|
|
1166
|
+
a.href = url;
|
|
1167
|
+
a.download = filename;
|
|
1168
|
+
document.body.appendChild(a);
|
|
1169
|
+
a.click();
|
|
1170
|
+
document.body.removeChild(a);
|
|
1171
|
+
URL.revokeObjectURL(url);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
debounce(func, wait) {
|
|
1175
|
+
let timeout;
|
|
1176
|
+
return function executedFunction(...args) {
|
|
1177
|
+
const later = () => {
|
|
1178
|
+
clearTimeout(timeout);
|
|
1179
|
+
func(...args);
|
|
1180
|
+
};
|
|
1181
|
+
clearTimeout(timeout);
|
|
1182
|
+
timeout = setTimeout(later, wait);
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
toggleCSSEditor() {
|
|
1187
|
+
const isVisible = this.cssContent.style.display !== 'none';
|
|
1188
|
+
|
|
1189
|
+
if (isVisible) {
|
|
1190
|
+
// Hide CSS editor
|
|
1191
|
+
this.cssContent.style.display = 'none';
|
|
1192
|
+
this.cssReset.style.display = 'none';
|
|
1193
|
+
this.cssApply.style.display = 'none';
|
|
1194
|
+
this.cssToggle.textContent = '📝 Edit CSS';
|
|
1195
|
+
} else {
|
|
1196
|
+
// Show CSS editor
|
|
1197
|
+
this.cssContent.style.display = 'block';
|
|
1198
|
+
this.cssReset.style.display = 'inline-block';
|
|
1199
|
+
this.cssApply.style.display = 'inline-block';
|
|
1200
|
+
this.cssToggle.textContent = '🔽 Hide CSS';
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
resetCSS() {
|
|
1205
|
+
this.cssEditor.value = '';
|
|
1206
|
+
this.customCSS = '';
|
|
1207
|
+
this.applyCSS();
|
|
1208
|
+
this.showMessage('CSS reset successfully', 'success');
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
applyCSS() {
|
|
1212
|
+
this.customCSS = this.cssEditor.value;
|
|
1213
|
+
this.updatePreviewWithCSS();
|
|
1214
|
+
// No mostrar mensaje en auto-apply para evitar spam
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
handleCSSFileUpload(event) {
|
|
1218
|
+
const file = event.target.files[0];
|
|
1219
|
+
if (!file) return;
|
|
1220
|
+
|
|
1221
|
+
if (!file.name.toLowerCase().endsWith('.css')) {
|
|
1222
|
+
this.showMessage('Please select a valid CSS file (.css)', 'error');
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
const reader = new FileReader();
|
|
1227
|
+
reader.onload = (e) => {
|
|
1228
|
+
try {
|
|
1229
|
+
const content = e.target.result;
|
|
1230
|
+
this.cssEditor.value = content;
|
|
1231
|
+
this.cssExampleSelect.value = '';
|
|
1232
|
+
this.applyCSS();
|
|
1233
|
+
this.showMessage(`📁 CSS file "${file.name}" loaded successfully`, 'success');
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
this.showMessage('Error reading CSS file: ' + error.message, 'error');
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
reader.onerror = () => {
|
|
1240
|
+
this.showMessage('Error reading CSS file. Please try again.', 'error');
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
reader.readAsText(file);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
getCSSExamples() {
|
|
1247
|
+
return {
|
|
1248
|
+
'modern': `/* Modern Clean Style */
|
|
1249
|
+
.preview-container {
|
|
1250
|
+
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
1251
|
+
line-height: 1.6;
|
|
1252
|
+
color: #2c3e50;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
h1 {
|
|
1256
|
+
color: #2980b9;
|
|
1257
|
+
border-bottom: 3px solid #3498db;
|
|
1258
|
+
padding-bottom: 10px;
|
|
1259
|
+
font-weight: 300;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
h2 {
|
|
1263
|
+
color: #34495e;
|
|
1264
|
+
border-left: 4px solid #3498db;
|
|
1265
|
+
padding-left: 16px;
|
|
1266
|
+
margin-top: 2em;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
p {
|
|
1270
|
+
margin-bottom: 1.5em;
|
|
1271
|
+
text-align: left;
|
|
1272
|
+
}`,
|
|
1273
|
+
|
|
1274
|
+
'classic': `/* Classic Legal Document */
|
|
1275
|
+
.preview-container {
|
|
1276
|
+
font-family: 'Times New Roman', serif;
|
|
1277
|
+
line-height: 1.8;
|
|
1278
|
+
color: #000;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
h1 {
|
|
1282
|
+
text-align: center;
|
|
1283
|
+
text-transform: uppercase;
|
|
1284
|
+
letter-spacing: 2px;
|
|
1285
|
+
border-bottom: double 3px #000;
|
|
1286
|
+
padding-bottom: 15px;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
h2 {
|
|
1290
|
+
font-variant: small-caps;
|
|
1291
|
+
border-bottom: 1px solid #666;
|
|
1292
|
+
margin-top: 2em;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
p {
|
|
1296
|
+
text-align: justify;
|
|
1297
|
+
text-indent: 2em;
|
|
1298
|
+
margin-bottom: 1em;
|
|
1299
|
+
}`,
|
|
1300
|
+
|
|
1301
|
+
'minimal': `/* Minimal Style */
|
|
1302
|
+
.preview-container {
|
|
1303
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
1304
|
+
line-height: 1.7;
|
|
1305
|
+
color: #333;
|
|
1306
|
+
max-width: none;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
h1 {
|
|
1310
|
+
font-size: 1.8em;
|
|
1311
|
+
font-weight: 600;
|
|
1312
|
+
color: #000;
|
|
1313
|
+
border: none;
|
|
1314
|
+
margin-bottom: 2em;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
h2 {
|
|
1318
|
+
font-size: 1.3em;
|
|
1319
|
+
font-weight: 500;
|
|
1320
|
+
color: #555;
|
|
1321
|
+
border: none;
|
|
1322
|
+
margin-top: 2.5em;
|
|
1323
|
+
margin-bottom: 1em;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
p {
|
|
1327
|
+
margin-bottom: 1.2em;
|
|
1328
|
+
text-align: left;
|
|
1329
|
+
}`
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
loadCSSExample(exampleKey) {
|
|
1334
|
+
if (!exampleKey) return;
|
|
1335
|
+
|
|
1336
|
+
const examples = this.getCSSExamples();
|
|
1337
|
+
const example = examples[exampleKey];
|
|
1338
|
+
|
|
1339
|
+
if (example) {
|
|
1340
|
+
this.cssEditor.value = example;
|
|
1341
|
+
this.cssExampleSelect.value = exampleKey;
|
|
1342
|
+
this.applyCSS();
|
|
1343
|
+
this.showMessage(`🎨 CSS example "${exampleKey}" loaded`, 'success');
|
|
1344
|
+
} else {
|
|
1345
|
+
this.showMessage('CSS example not found.', 'error');
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
initColumnResizing() {
|
|
1350
|
+
const container = document.querySelector('.editor-container');
|
|
1351
|
+
let isResizing = false;
|
|
1352
|
+
let currentResizer = null;
|
|
1353
|
+
let startX = 0;
|
|
1354
|
+
let startWidths = [1, 1, 1]; // Start with equal widths
|
|
1355
|
+
|
|
1356
|
+
const resizers = [this.resizer1, this.resizer2];
|
|
1357
|
+
|
|
1358
|
+
resizers.forEach((resizer, index) => {
|
|
1359
|
+
if (!resizer) return;
|
|
1360
|
+
|
|
1361
|
+
resizer.addEventListener('mousedown', (e) => {
|
|
1362
|
+
isResizing = true;
|
|
1363
|
+
currentResizer = resizer;
|
|
1364
|
+
startX = e.clientX;
|
|
1365
|
+
|
|
1366
|
+
// Parse current fr values from the style
|
|
1367
|
+
const currentStyle = container.style.gridTemplateColumns;
|
|
1368
|
+
if (currentStyle && currentStyle.includes('fr')) {
|
|
1369
|
+
const matches = currentStyle.match(/(\d*\.?\d+)fr/g);
|
|
1370
|
+
if (matches && matches.length === 3) {
|
|
1371
|
+
startWidths = matches.map(match => parseFloat(match.replace('fr', '')));
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
1376
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
1377
|
+
e.preventDefault();
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
const handleMouseMove = (e) => {
|
|
1382
|
+
if (!isResizing || !currentResizer) return;
|
|
1383
|
+
|
|
1384
|
+
const deltaX = e.clientX - startX;
|
|
1385
|
+
const containerWidth = container.offsetWidth;
|
|
1386
|
+
// Convert pixels to fraction units (balanced sensitivity)
|
|
1387
|
+
const deltaFraction = (deltaX / containerWidth) * 4;
|
|
1388
|
+
|
|
1389
|
+
let newWidths = [...startWidths];
|
|
1390
|
+
const minWidth = 0.3; // Minimum 30% width
|
|
1391
|
+
|
|
1392
|
+
if (currentResizer === this.resizer1) {
|
|
1393
|
+
// Resizing between columns 1 and 2
|
|
1394
|
+
newWidths[0] = Math.max(minWidth, startWidths[0] + deltaFraction);
|
|
1395
|
+
newWidths[1] = Math.max(minWidth, startWidths[1] - deltaFraction);
|
|
1396
|
+
} else if (currentResizer === this.resizer2) {
|
|
1397
|
+
// Resizing between columns 2 and 3
|
|
1398
|
+
newWidths[1] = Math.max(minWidth, startWidths[1] + deltaFraction);
|
|
1399
|
+
newWidths[2] = Math.max(minWidth, startWidths[2] - deltaFraction);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// Apply the new column widths
|
|
1403
|
+
container.style.gridTemplateColumns = `${newWidths[0]}fr 4px ${newWidths[1]}fr 4px ${newWidths[2]}fr`;
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1406
|
+
const handleMouseUp = () => {
|
|
1407
|
+
isResizing = false;
|
|
1408
|
+
currentResizer = null;
|
|
1409
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
1410
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
updatePreviewWithCSS() {
|
|
1415
|
+
// Remove existing custom style tag if it exists
|
|
1416
|
+
const existingStyle = document.getElementById('customCSS');
|
|
1417
|
+
if (existingStyle) {
|
|
1418
|
+
existingStyle.remove();
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// Add new custom CSS if provided
|
|
1422
|
+
if (this.customCSS.trim()) {
|
|
1423
|
+
const styleTag = document.createElement('style');
|
|
1424
|
+
styleTag.id = 'customCSS';
|
|
1425
|
+
styleTag.textContent = `
|
|
1426
|
+
.preview-container {
|
|
1427
|
+
${this.customCSS}
|
|
1428
|
+
}
|
|
1429
|
+
`;
|
|
1430
|
+
document.head.appendChild(styleTag);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
toggleBaseStyles() {
|
|
1435
|
+
this.baseStylesEnabled = !this.baseStylesEnabled;
|
|
1436
|
+
this.updateBaseStyles();
|
|
1437
|
+
|
|
1438
|
+
// Update button appearance
|
|
1439
|
+
this.toggleStylesBtn.style.opacity = this.baseStylesEnabled ? '1' : '0.5';
|
|
1440
|
+
this.toggleStylesBtn.title = this.baseStylesEnabled ?
|
|
1441
|
+
'Disable base styles' : 'Enable base styles';
|
|
1442
|
+
|
|
1443
|
+
this.showMessage(
|
|
1444
|
+
this.baseStylesEnabled ? 'Base styles enabled' : 'Base styles disabled',
|
|
1445
|
+
'info'
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
updateBaseStyles() {
|
|
1450
|
+
if (this.baseStylesEnabled) {
|
|
1451
|
+
this.preview.classList.add('styled-preview');
|
|
1452
|
+
} else {
|
|
1453
|
+
this.preview.classList.remove('styled-preview');
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// Initialize app when DOM is ready
|
|
1459
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
1460
|
+
new LegalMarkdownApp();
|
|
1461
|
+
});
|
|
1462
|
+
</script>
|
|
1463
|
+
</body>
|
|
1464
|
+
|
|
1465
|
+
</html>
|