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(); +}; + });