diff --git a/dom.js b/dom.js
index 4c0b32d..5e4d228 100644
--- a/dom.js
+++ b/dom.js
@@ -57,6 +57,10 @@ const
class ASN1DOM extends ASN1 {
+ buf2hex(buffer) {
+ return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, '0')).join(' ');
+ }
+
toDOM(spaces) {
spaces = spaces || '';
let isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06) || (this.tag.tagNumber == 0x0D));
@@ -187,6 +191,15 @@ class ASN1DOM extends ASN1 {
this.className = 'hex';
}
};
+ // handler to copy the complete hex dump into the clipboard
+ node.onclick = function (event) {
+ let contextMenu = document.getElementById('contextmenu');
+ contextMenu.style.left = event.pageX + "px";
+ contextMenu.style.top = event.pageY + "px";
+ contextMenu.style.visibility = 'visible';
+ document.getElementById('contextmenu').node = this;
+ event.stopPropagation();
+ };
if (root == node) {
let lineStart = this.posStart() & 0xF;
if (lineStart != 0) {
@@ -200,6 +213,9 @@ class ASN1DOM extends ASN1 {
node.appendChild(skip);
}
}
+ // set the current start and end position as an attribute at the node to know the selected area
+ node.setAttribute('pos', this.posStart());
+ node.setAttribute('end', this.posEnd());
this.toHexDOM_sub(node, 'tag', this.stream, this.posStart(), this.posLen());
this.toHexDOM_sub(node, (this.length >= 0) ? 'dlen' : 'ulen', this.stream, this.posLen(), this.posContent());
if (this.sub === null) {
diff --git a/index.css b/index.css
index c57b12d..b3601b1 100644
--- a/index.css
+++ b/index.css
@@ -212,3 +212,25 @@ header {
#dump .hexCurrent .dlen { color: #004040; }
#file { display: none; }
#area { width: 100%; }
+
+#contextmenu {
+ position: absolute;
+ visibility: hidden;
+ top: 0;
+ left: 0;
+ padding: 2px;
+ background-color: var(--button-bg-color);
+ border: 1px solid var(--button-bg-color);
+ z-index: 2;
+}
+
+#contextmenu > button {
+ width: 120px;
+ background-color: var(--button-bg-color);
+ color: var(--main-text-color);
+ border: 1px solid var(--button-border-color);
+ text-align: left;
+}
+#contextmenu > button:hover {
+ background-color: var(--button-bghover-color);
+}
diff --git a/index.html b/index.html
index 7b7a9f3..11c610a 100644
--- a/index.html
+++ b/index.html
@@ -8,6 +8,7 @@
+
ASN.1 JavaScript decoder
diff --git a/index.js b/index.js
index fc468e0..70c2a52 100644
--- a/index.js
+++ b/index.js
@@ -5,6 +5,7 @@
'use strict';
const
+ ASN1 = require('./asn1'),
ASN1DOM = require('./dom'),
Base64 = require('./base64'),
Hex = require('./hex'),
@@ -50,6 +51,9 @@ function show(asn1) {
if (wantHex.checked) dump.appendChild(asn1.toHexDOM(undefined, trimHex.checked));
}
function decode(der, offset) {
+ // store the DER buffer of asn1 in window to copy it completely into clipboard on dumpcopy
+ window.derBuffer = der;
+
offset = offset || 0;
try {
const asn1 = ASN1DOM.decode(der, offset);
@@ -236,4 +240,46 @@ selectTag.onchange = function (ev) {
window.location.href = 'https://rawcdn.githack.com/lapo-luchini/asn1js/' + tag + '/index.html';
};
+// register context menu function
+document.getElementById('btnCopyHex').onclick = function (event) {
+ let contextMenu = document.getElementById('contextmenu');
+ let node = contextMenu.node;
+ const pos = parseInt(node.getAttribute('pos'));
+ const end = parseInt(node.getAttribute('end'));
+ const hex = node.asn1.buf2hex(window.derBuffer.subarray(pos, end));
+ navigator.clipboard.writeText(hex);
+ contextMenu.style.visibility = 'hidden';
+ event.stopPropagation();
+};
+
+document.getElementById('btnCopyString').onclick = function (event) {
+ let contextMenu = document.getElementById('contextmenu');
+ let node = contextMenu.node;
+ const pos = parseInt(node.getAttribute('pos'));
+ const end = parseInt(node.getAttribute('end'));
+ let result = ASN1.decode(window.derBuffer.subarray(pos, end));
+ let type = result.typeName();
+ switch (type) {
+ case 'SET':
+ case 'SEQUENCE':
+ alert('Selected value is not a String!');
+ break;
+ default:
+ navigator.clipboard.writeText(result.content());
+ }
+ contextMenu.style.visibility = 'hidden';
+ event.stopPropagation();
+};
+
+document.getElementById('btnCopyPretty').onclick = function (event) {
+ let contextMenu = document.getElementById('contextmenu');
+ let node = contextMenu.node;
+ const pos = parseInt(node.getAttribute('pos'));
+ const end = parseInt(node.getAttribute('end'));
+ let result = ASN1.decode(window.derBuffer.subarray(pos, end));
+ navigator.clipboard.writeText(result.toPrettyString());
+ contextMenu.style.visibility = 'hidden';
+ event.stopPropagation();
+};
+
});