0
# DOM Visualization
1
2
Browser-specific functionality for creating interactive ASN.1 structure visualizations with DOM elements and context menus.
3
4
## Capabilities
5
6
### ASN1DOM Class
7
8
Extended ASN.1 parser with DOM rendering capabilities for web browsers, providing interactive tree views of ASN.1 structures.
9
10
```javascript { .api }
11
/**
12
* ASN.1 parser with DOM rendering capabilities
13
* Extends ASN1 class with browser-specific visualization methods
14
*/
15
class ASN1DOM extends ASN1 {
16
/**
17
* Generate DOM element representation of ASN.1 structure
18
* Creates an interactive tree view with expandable/collapsible nodes
19
* @param spaces - Indentation string for formatting (optional)
20
* @returns HTML list item element representing the ASN.1 structure
21
*/
22
toDOM(spaces?: string): HTMLLIElement;
23
24
/**
25
* Generate hexadecimal DOM display with formatting and highlighting
26
* @param start - Starting position for hex display (optional)
27
* @param trimmedHex - Whether to trim hex display for large content (optional)
28
* @returns HTML element containing formatted hex dump
29
*/
30
toHexDOM(start?: number, trimmedHex?: boolean): HTMLElement;
31
}
32
```
33
34
**Usage Examples:**
35
36
```javascript
37
import { ASN1DOM } from '@lapo/asn1js/dom.js';
38
import { Base64 } from '@lapo/asn1js/base64.js';
39
40
// Parse certificate and create DOM visualization
41
const certPem = `-----BEGIN CERTIFICATE-----
42
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3YkQOL4OqPlzJGPbhUKZ
43
...
44
-----END CERTIFICATE-----`;
45
46
const certData = Base64.unarmor(certPem);
47
const cert = ASN1DOM.decode(certData);
48
49
// Create DOM tree view
50
const treeContainer = document.getElementById('asn1-tree');
51
const ul = document.createElement('ul');
52
ul.className = 'treecollapse';
53
ul.appendChild(cert.toDOM());
54
treeContainer.appendChild(ul);
55
56
// Create hex dump view
57
const hexContainer = document.getElementById('hex-dump');
58
hexContainer.appendChild(cert.toHexDOM(undefined, true));
59
60
// Access the ASN1 object from DOM elements
61
ul.addEventListener('click', (event) => {
62
const listItem = event.target.closest('li');
63
if (listItem && listItem.asn1) {
64
console.log('Clicked ASN.1 element:', listItem.asn1.typeName());
65
console.log('Content:', listItem.asn1.content(100));
66
}
67
});
68
```
69
70
### Context Menu Integration
71
72
Utility function for binding context menus to ASN.1 DOM elements.
73
74
```javascript { .api }
75
/**
76
* Bind context menu functionality to DOM nodes
77
* Adds right-click context menu with ASN.1-specific options
78
* @param node - DOM node to bind context menu to
79
*/
80
function bindContextMenu(node: HTMLElement): void;
81
```
82
83
**Usage Examples:**
84
85
```javascript
86
import { ASN1DOM } from '@lapo/asn1js/dom.js';
87
import { bindContextMenu } from '@lapo/asn1js/context.js';
88
import { Hex } from '@lapo/asn1js/hex.js';
89
90
// Create ASN.1 structure with context menu
91
const data = Hex.decode('300C06032B6570050000');
92
const asn1 = ASN1DOM.decode(data);
93
94
const domElement = asn1.toDOM();
95
bindContextMenu(domElement);
96
97
// Add to page
98
document.body.appendChild(domElement);
99
100
// Context menu will provide options like:
101
// - Copy hex value
102
// - Copy base64 value
103
// - Show OID description (for OID elements)
104
// - Export structure
105
```
106
107
### Complete Web Interface Example
108
109
Example showing how to build a complete ASN.1 viewer web interface.
110
111
```javascript
112
import { ASN1DOM } from '@lapo/asn1js/dom.js';
113
import { Base64 } from '@lapo/asn1js/base64.js';
114
import { Hex } from '@lapo/asn1js/hex.js';
115
import { bindContextMenu } from '@lapo/asn1js/context.js';
116
117
class ASN1Viewer {
118
constructor(containerElement) {
119
this.container = containerElement;
120
this.setupUI();
121
}
122
123
setupUI() {
124
this.container.innerHTML = `
125
<div class="asn1-viewer">
126
<div class="controls">
127
<textarea id="input" placeholder="Paste PEM, Base64, or Hex data here..."></textarea>
128
<button id="decode">Decode</button>
129
<button id="clear">Clear</button>
130
</div>
131
<div class="output">
132
<div id="tree-view" class="tree-container"></div>
133
<div id="hex-view" class="hex-container"></div>
134
</div>
135
</div>
136
`;
137
138
this.inputArea = this.container.querySelector('#input');
139
this.treeView = this.container.querySelector('#tree-view');
140
this.hexView = this.container.querySelector('#hex-view');
141
142
this.container.querySelector('#decode').onclick = () => this.decode();
143
this.container.querySelector('#clear').onclick = () => this.clear();
144
}
145
146
decode() {
147
try {
148
const input = this.inputArea.value.trim();
149
if (!input) return;
150
151
let binaryData;
152
153
// Auto-detect format
154
if (/^[0-9A-Fa-f\s]+$/.test(input)) {
155
binaryData = Hex.decode(input);
156
} else {
157
binaryData = Base64.unarmor(input);
158
}
159
160
const asn1 = ASN1DOM.decode(binaryData);
161
this.displayStructure(asn1);
162
163
} catch (error) {
164
this.showError(error.message);
165
}
166
}
167
168
displayStructure(asn1) {
169
// Clear previous content
170
this.treeView.innerHTML = '';
171
this.hexView.innerHTML = '';
172
173
// Create tree view
174
const ul = document.createElement('ul');
175
ul.className = 'treecollapse';
176
const li = asn1.toDOM();
177
bindContextMenu(li);
178
ul.appendChild(li);
179
this.treeView.appendChild(ul);
180
181
// Create hex view
182
const hexElement = asn1.toHexDOM(undefined, true);
183
this.hexView.appendChild(hexElement);
184
185
// Add click handlers for tree navigation
186
this.addTreeInteractivity(ul);
187
}
188
189
addTreeInteractivity(treeElement) {
190
treeElement.addEventListener('click', (event) => {
191
const target = event.target;
192
193
// Toggle collapse/expand
194
if (target.classList.contains('toggle')) {
195
const li = target.closest('li');
196
li.classList.toggle('collapsed');
197
event.stopPropagation();
198
return;
199
}
200
201
// Highlight selected element
202
treeElement.querySelectorAll('li.selected').forEach(el => {
203
el.classList.remove('selected');
204
});
205
206
const li = target.closest('li');
207
if (li && li.asn1) {
208
li.classList.add('selected');
209
this.showElementDetails(li.asn1);
210
}
211
});
212
}
213
214
showElementDetails(asn1) {
215
// Show detailed information about the selected element
216
const details = {
217
type: asn1.typeName(),
218
position: `${asn1.posStart()}-${asn1.posEnd()}`,
219
length: asn1.length,
220
content: asn1.content(200)
221
};
222
223
console.log('Selected ASN.1 element:', details);
224
225
// Could update a details panel in the UI
226
// this.updateDetailsPanel(details);
227
}
228
229
showError(message) {
230
this.treeView.innerHTML = `<div class="error">Error: ${message}</div>`;
231
this.hexView.innerHTML = '';
232
}
233
234
clear() {
235
this.inputArea.value = '';
236
this.treeView.innerHTML = '';
237
this.hexView.innerHTML = '';
238
}
239
}
240
241
// Usage
242
const viewerContainer = document.getElementById('asn1-viewer-container');
243
const viewer = new ASN1Viewer(viewerContainer);
244
```
245
246
### CSS Styling for ASN.1 DOM Elements
247
248
Recommended CSS classes for styling ASN.1 DOM visualizations.
249
250
```css
251
/* Tree view styling */
252
.treecollapse {
253
font-family: monospace;
254
font-size: 12px;
255
list-style: none;
256
padding-left: 0;
257
}
258
259
.treecollapse li {
260
margin: 2px 0;
261
padding-left: 16px;
262
position: relative;
263
}
264
265
.treecollapse li:before {
266
content: "▼";
267
position: absolute;
268
left: 0;
269
cursor: pointer;
270
color: #666;
271
}
272
273
.treecollapse li.collapsed:before {
274
content: "▶";
275
}
276
277
.treecollapse li.collapsed > ul {
278
display: none;
279
}
280
281
/* ASN.1 element styling */
282
.head {
283
cursor: pointer;
284
}
285
286
.head:hover {
287
background-color: #f0f0f0;
288
}
289
290
.selected .head {
291
background-color: #e6f3ff;
292
}
293
294
/* Type and content styling */
295
.name.id {
296
color: #0066cc;
297
font-weight: bold;
298
}
299
300
.name.type {
301
color: #006600;
302
}
303
304
.preview {
305
color: #666;
306
font-style: italic;
307
}
308
309
.oid.description {
310
color: #8b4513;
311
font-size: 11px;
312
}
313
314
/* Hex view styling */
315
.hex-container {
316
font-family: monospace;
317
font-size: 11px;
318
white-space: pre;
319
background-color: #f8f8f8;
320
padding: 10px;
321
border: 1px solid #ddd;
322
overflow: auto;
323
}
324
325
.hex-address {
326
color: #666;
327
}
328
329
.hex-bytes {
330
color: #000;
331
}
332
333
.hex-ascii {
334
color: #0066cc;
335
}
336
337
/* Error styling */
338
.error {
339
color: #cc0000;
340
background-color: #ffe6e6;
341
padding: 10px;
342
border: 1px solid #cc0000;
343
border-radius: 4px;
344
}
345
```