1(typeof define != 'undefined' ? define : function (factory) { 'use strict';
2 if (typeof module == 'object') factory(function (name) { return require(name); });
3 else factory(function (name) { return window[name.substring(2)]; });
4})(function (require) {
5'use strict';
6
7const
8 ASN1DOM = require('./dom'),
9 Base64 = require('./base64'),
10 Hex = require('./hex'),
11 Defs = require('./defs'),
12 tags = require('./tags'),
13 maxLength = 10240,
14 reHex = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/,
15 tree = id('tree'),
16 dump = id('dump'),
17 wantHex = checkbox('wantHex'),
18 trimHex = checkbox('trimHex'),
19 wantDef = checkbox('wantDef'),
20 area = id('area'),
21 file = id('file'),
22 examples = id('examples'),
23 selectTheme = id('theme-select'),
24 selectDefs = id('definitions'),
25 selectTag = id('tags');
26
27let hash = null;
28
29if (!window.console || !window.console.log) // IE8 with closed developer tools
30 window.console = { log: function () {} };
31function id(elem) {
32 return document.getElementById(elem);
33}
34function text(el, string) {
35 if ('textContent' in el) el.textContent = string;
36 else el.innerText = string;
37}
38function checkbox(name) {
39 const el = id(name);
40 const cfg = localStorage.getItem(name);
41 if (cfg === 'false')
42 el.checked = false;
43 el.onchange = () => localStorage.setItem(name, el.checked);
44 return el;
45}
46function show(asn1) {
47 tree.innerHTML = '';
48 dump.innerHTML = '';
49 tree.appendChild(asn1.toDOM());
50 if (wantHex.checked) dump.appendChild(asn1.toHexDOM(undefined, trimHex.checked));
51}
52function decode(der, offset) {
53 offset = offset || 0;
54 try {
55 const asn1 = ASN1DOM.decode(der, offset);
56 if (wantDef.checked) {
57 selectDefs.innerHTML = '';
58 const types = Defs.commonTypes
59 .map(type => {
60 const stats = Defs.match(asn1, type);
61 return { type, match: stats.recognized / stats.total };
62 })
63 .sort((a, b) => b.match - a.match);
64 for (const t of types) {
65 t.element = document.createElement('option');
66 t.element.innerText = (t.match * 100).toFixed(1) + '% ' + t.type.description;
67 selectDefs.appendChild(t.element);
68 }
69 let not = document.createElement('option');
70 not.innerText = 'no definition';
71 selectDefs.appendChild(not);
72 Defs.match(asn1, types[0].type);
73 selectDefs.onchange = () => {
74 for (const t of types) {
75 if (t.element == selectDefs.selectedOptions[0]) {
76 Defs.match(asn1, t.type);
77 show(asn1);
78 return;
79 }
80 }
81 Defs.match(asn1, null);
82 show(asn1);
83 };
84 } else
85 selectDefs.innerHTML = '<option>no definition</option>';
86 show(asn1);
87 let b64 = der.length < maxLength ? asn1.toB64String() : '';
88 if (area.value === '') area.value = Base64.pretty(b64);
89 try {
90 window.location.hash = hash = '#' + b64;
91 } catch (e) {
92 // fails with "Access Denied" on IE with URLs longer than ~2048 chars
93 window.location.hash = hash = '#';
94 }
95 let endOffset = asn1.posEnd();
96 if (endOffset < der.length) {
97 let p = document.createElement('p');
98 p.innerText = 'Input contains ' + (der.length - endOffset) + ' more bytes to decode.';
99 let button = document.createElement('button');
100 button.innerText = 'try to decode';
101 button.onclick = function () {
102 decode(der, endOffset);
103 };
104 p.appendChild(button);
105 tree.insertBefore(p, tree.firstChild);
106 }
107 } catch (e) {
108 text(tree, e);
109 }
110}
111function decodeText(val) {
112 try {
113 let der = reHex.test(val) ? Hex.decode(val) : Base64.unarmor(val);
114 decode(der);
115 } catch (e) {
116 text(tree, e);
117 dump.innerHTML = '';
118 }
119}
120function decodeBinaryString(str) {
121 let der;
122 try {
123 if (reHex.test(str)) der = Hex.decode(str);
124 else if (Base64.re.test(str)) der = Base64.unarmor(str);
125 else der = str;
126 decode(der);
127 } catch (e) {
128 text(tree, 'Cannot decode file.');
129 dump.innerHTML = '';
130 }
131}
132// set up buttons
133id('butDecode').onclick = function () {
134 decodeText(area.value);
135};
136id('butClear').onclick = function () {
137 area.value = '';
138 file.value = '';
139 tree.innerHTML = '';
140 dump.innerHTML = '';
141 hash = window.location.hash = '';
142};
143id('butExample').onclick = function () {
144 console.log('Loading example:', examples.value);
145 let request = new XMLHttpRequest();
146 request.open('GET', 'examples/' + examples.value, true);
147 request.onreadystatechange = function () {
148 if (this.readyState !== 4) return;
149 if (this.status >= 200 && this.status < 400) {
150 area.value = this.responseText;
151 decodeText(this.responseText);
152 } else {
153 console.log('Error loading example.');
154 }
155 };
156 request.send();
157};
158// set dark theme depending on OS settings
159function setTheme() {
160 let storedTheme = localStorage.getItem('theme');
161 let theme = 'os';
162 if (storedTheme)
163 theme = storedTheme;
164 selectTheme.value = theme;
165 if (theme == 'os') {
166 let prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
167 theme = prefersDarkScheme.matches ? 'dark': 'light';
168 }
169 if (theme == 'dark') {
170 const css1 = id('theme-base');
171 const css2 = css1.cloneNode();
172 css2.id = 'theme-override';
173 css2.href = 'index-' + theme + '.css';
174 css1.parentElement.appendChild(css2);
175 } else {
176 const css2 = id('theme-override');
177 if (css2) css2.remove();
178 }
179}
180setTheme();
181selectTheme.addEventListener('change', function () {
182 localStorage.setItem('theme', selectTheme.value);
183 setTheme();
184});
185// this is only used if window.FileReader
186function read(f) {
187 area.value = ''; // clear text area, will get b64 content
188 let r = new FileReader();
189 r.onloadend = function () {
190 if (r.error) alert("Your browser couldn't read the specified file (error code " + r.error.code + ').');
191 else decodeBinaryString(r.result);
192 };
193 r.readAsBinaryString(f);
194}
195function load() {
196 if (file.files.length === 0) alert('Select a file to load first.');
197 else read(file.files[0]);
198}
199function loadFromHash() {
200 if (window.location.hash && window.location.hash != hash) {
201 hash = window.location.hash;
202 // Firefox is not consistent with other browsers and returns an
203 // already-decoded hash string so we risk double-decoding here,
204 // but since % is not allowed in base64 nor hexadecimal, it's ok
205 let val = decodeURIComponent(hash.substr(1));
206 if (val.length) decodeText(val);
207 }
208}
209function stop(e) {
210 e.stopPropagation();
211 e.preventDefault();
212}
213function dragAccept(e) {
214 stop(e);
215 if (e.dataTransfer.files.length > 0) read(e.dataTransfer.files[0]);
216}
217// main
218if ('onhashchange' in window) window.onhashchange = loadFromHash;
219loadFromHash();
220document.ondragover = stop;
221document.ondragleave = stop;
222if ('FileReader' in window && 'readAsBinaryString' in new FileReader()) {
223 file.style.display = 'block';
224 file.onchange = load;
225 document.ondrop = dragAccept;
226}
227for (let tag in tags) {
228 let date = tags[tag];
229 let el = document.createElement('option');
230 el.value = tag;
231 el.innerText = date + ' ' + tag;
232 selectTag.appendChild(el);
233}
234selectTag.onchange = function (ev) {
235 let tag = ev.target.selectedOptions[0].value;
236 window.location.href = 'https://rawcdn.githack.com/lapo-luchini/asn1js/' + tag + '/index.html';
237};
238
239});