1 | window.module = {};
|
2 |
|
3 | const Cookies = require('js-cookie');
|
4 | const frontendApi = require('./frontend-api.js')({crdsUrl: '/id/api'});
|
5 | const base64 = require('./base64');
|
6 |
|
7 | window.$ = s => document.querySelectorAll(s);
|
8 |
|
9 | const baseUrl = '/id';
|
10 | const pages = {
|
11 | login: () => {
|
12 |
|
13 |
|
14 | const pageEl = $('#login-page')[0];
|
15 | pageEl.style.display = null;
|
16 |
|
17 | return () => {
|
18 |
|
19 | pageEl.style.display = 'none';
|
20 |
|
21 | $('#login-input')[0].value = '';
|
22 | };
|
23 | },
|
24 | account: () => {
|
25 |
|
26 |
|
27 | header.setTab('avatar');
|
28 | const pageEl = $('#account-page')[0];
|
29 | pageEl.style.display = null;
|
30 |
|
31 | return () => {
|
32 |
|
33 | pageEl.style.display = 'none';
|
34 | };
|
35 | },
|
36 | asset: () => {
|
37 |
|
38 |
|
39 | header.setTab('avatar');
|
40 | const pageEl = $('#asset-page')[0];
|
41 | pageEl.style.display = null;
|
42 |
|
43 | const assetReadmeEl = $('#asset-readme')[0];
|
44 | fetch(baseUrl + '/api/data/' + _getBaseAsset(assetReadmeEl.asset))
|
45 | .then(_resText)
|
46 | .then(readme => {
|
47 | _setReadmeText(readme);
|
48 | })
|
49 | .catch(err => {
|
50 | console.warn(err);
|
51 | });
|
52 |
|
53 | return () => {
|
54 |
|
55 | pageEl.style.display = 'none';
|
56 |
|
57 |
|
58 | $('#asset-readme-form')[0].style.display = '';
|
59 | $('#send-asset-address-input')[0].value = '';
|
60 | $('#send-asset-quantity-input')[0].value = '';
|
61 | $('#mint-asset-form')[0].style.display = '';
|
62 | $('#price-asset-form')[0].style.display = '';
|
63 | $('#mint-subasset-name-input')[0].value = '';
|
64 | $('#mint-quantity-input')[0].value = '';
|
65 | };
|
66 | },
|
67 | createAsset: () => {
|
68 |
|
69 |
|
70 | header.setTab('avatar');
|
71 | const pageEl = $('#create-asset-page')[0];
|
72 | pageEl.style.display = null;
|
73 |
|
74 | const createAssetNameInput = $('#create-asset-name-input')[0];
|
75 | createAssetNameInput.value = '';
|
76 | createAssetNameInput.focus();
|
77 | createAssetNameInput.select();
|
78 |
|
79 | return () => {
|
80 |
|
81 | pageEl.style.display = 'none';
|
82 |
|
83 | $('#create-asset-name-input')[0].value = '';
|
84 | };
|
85 | },
|
86 | buyAsset: () => {
|
87 |
|
88 |
|
89 | header.setTab('avatar');
|
90 | const pageEl = $('#buy-asset-page')[0];
|
91 | pageEl.style.display = null;
|
92 |
|
93 | const buyAssetNameInput = $('#buy-asset-name-input')[0];
|
94 | buyAssetNameInput.value = '';
|
95 | buyAssetNameInput.focus();
|
96 | buyAssetNameInput.select();
|
97 |
|
98 | return () => {
|
99 |
|
100 | pageEl.style.display = 'none';
|
101 |
|
102 | $('#buy-asset-name-input')[0].value = '';
|
103 | $('#buy-asset-quantity-input')[0].value = '';
|
104 | };
|
105 | },
|
106 | confirmPurchase: () => {
|
107 |
|
108 |
|
109 | header.setTab('avatar');
|
110 | const pageEl = $('#confirm-purchase-page')[0];
|
111 | pageEl.style.display = null;
|
112 |
|
113 | return () => {
|
114 | statusbarEl.style.display = 'none';
|
115 | pageEl.style.display = 'none';
|
116 |
|
117 | $('#confirm-purchase-asset')[0].innerText = '';
|
118 | $('#confirm-purchase-price')[0].innerText = '';
|
119 | };
|
120 | },
|
121 | keys: () => {
|
122 |
|
123 |
|
124 | header.setTab('keys');
|
125 | const pageEl = $('#keys-page')[0];
|
126 | pageEl.style.display = null;
|
127 |
|
128 | return () => {
|
129 |
|
130 | pageEl.style.display = 'none';
|
131 |
|
132 | const showSecrets = $('#show-secrets')[0];
|
133 | const hideSecrets = $('#hide-secrets')[0];
|
134 | const secretValue = $('#secret-value')[0];
|
135 | showSecrets.style.display = 'block';
|
136 | hideSecrets.style.display = 'none';
|
137 | secretValue.classList.remove('visible');
|
138 | };
|
139 | },
|
140 | mining: () => {
|
141 |
|
142 |
|
143 | header.setTab('mining');
|
144 | const pageEl = $('#mining-page')[0];
|
145 | pageEl.style.display = null;
|
146 |
|
147 | fetch(baseUrl + '/api/minedBlocks')
|
148 | .then(_resJson)
|
149 | .then(minedBlocks => {
|
150 | $('#blocks-mined')[0].innerText = minedBlocks;
|
151 | })
|
152 | .catch(err => {
|
153 | console.warn(err);
|
154 | });
|
155 |
|
156 | return () => {
|
157 |
|
158 | pageEl.style.display = 'none';
|
159 | };
|
160 | },
|
161 | monitor: () => {
|
162 |
|
163 |
|
164 | header.setTab('monitor');
|
165 | const pageEl = $('#monitor-page')[0];
|
166 | pageEl.style.display = null;
|
167 |
|
168 | return () => {
|
169 |
|
170 | pageEl.style.display = 'none';
|
171 |
|
172 | const blocksData = $('#blocks-data')[0];
|
173 | blocksData.innerText = '';
|
174 | const mempoolData = $('#mempool-data')[0];
|
175 | mempoolData.innerText = '';
|
176 |
|
177 | const showSecrets = $('#show-secrets')[0];
|
178 | const hideSecrets = $('#hide-secrets')[0];
|
179 | const secretValue = $('#secret-value')[0];
|
180 | showSecrets.style.display = 'block';
|
181 | hideSecrets.style.display = 'none';
|
182 | secretValue.classList.remove('visible');
|
183 | };
|
184 | },
|
185 | };
|
186 |
|
187 | const _getBaseAsset = asset => {
|
188 | const match = asset.match(/^(.+):mint$/);
|
189 | return match ? match[1] : asset;
|
190 | };
|
191 | const _setReadmeText = readme => {
|
192 | const assetReadmeEl = $('#asset-readme')[0];
|
193 |
|
194 | if (readme) {
|
195 | assetReadmeEl.innerText = readme;
|
196 |
|
197 | assetReadmeEl.text = readme;
|
198 | } else {
|
199 | assetReadmeEl.innerHTML = '';
|
200 | const readmePlaceholderEl = document.createElement('div');
|
201 | readmePlaceholderEl.classList.add('readme-placeholder');
|
202 | readmePlaceholderEl.innerText = 'No readme';
|
203 | assetReadmeEl.appendChild(readmePlaceholderEl);
|
204 |
|
205 | assetReadmeEl.text = '';
|
206 | }
|
207 | };
|
208 | const _nop = () => {};
|
209 | const _resJson = res => {
|
210 | if (res.status >= 200 && res.status < 300) {
|
211 | return res.json();
|
212 | } else {
|
213 | return Promise.reject({
|
214 | status: res.status,
|
215 | stack: 'API returned invalid status code: ' + res.status,
|
216 | });
|
217 | }
|
218 | };
|
219 | const _resText = res => {
|
220 | if (res.status >= 200 && res.status < 300) {
|
221 | return res.text();
|
222 | } else if (res.status === 404) {
|
223 | return Promise.resolve('');
|
224 | } else {
|
225 | return Promise.reject({
|
226 | status: res.status,
|
227 | stack: 'API returned invalid status code: ' + res.status,
|
228 | });
|
229 | }
|
230 | };
|
231 | const _resBlob = res => {
|
232 | if (res.status >= 200 && res.status < 300) {
|
233 | return res.blob();
|
234 | } else {
|
235 | return Promise.reject({
|
236 | status: res.status,
|
237 | stack: 'API returned invalid status code: ' + res.status,
|
238 | });
|
239 | }
|
240 | };
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 | const _boot = () => {
|
252 | header.setNotification(null, `Items are stored on the blockchain. Use them on any VR server. <a href="http://zeovr.wikia.com/wiki/Items">Learn more</a>`);
|
253 | header.onlogout = e => {
|
254 | Cookies.remove('privateKey');
|
255 | Cookies.remove('data');
|
256 |
|
257 | };
|
258 |
|
259 | let pageCleanup = null;
|
260 | const _setPage = page => {
|
261 | if (pageCleanup) {
|
262 | pageCleanup();
|
263 | pageCleanup = null;
|
264 | }
|
265 |
|
266 | pageCleanup = pages[page]();
|
267 | };
|
268 |
|
269 | let balances = [];
|
270 | const _login = privateKey => {
|
271 | if (privateKey) {
|
272 | Cookies.set('privateKey', privateKey, {expires: 365});
|
273 |
|
274 | const publicKey = base64.encode(frontendApi.getPublicKey(base64.decode(privateKey)));
|
275 | secretValue.innerText = JSON.stringify({
|
276 | publicKey: publicKey,
|
277 | privateKey: privateKey,
|
278 | }, null, 2);
|
279 |
|
280 | |
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 | }
|
301 |
|
302 | return Promise.resolve();
|
303 | };
|
304 | const _loadAsync = privateKey => {
|
305 | const address = privateKey ? frontendApi.getAddress(base64.decode(privateKey)) : null;
|
306 |
|
307 | return Promise.all([
|
308 | address ? frontendApi.requestUnconfirmedBalances(address) : Promise.resolve([]),
|
309 | ])
|
310 | .then(([
|
311 | newBalances,
|
312 | ]) => {
|
313 | const _getAssetSrc = (asset, quantity) => {
|
314 | const baseAsset = _getBaseAsset(asset);
|
315 | return `\
|
316 | <div class=icon>
|
317 | <img class=img src="${creaturejs.makeStaticCreature('asset:' + baseAsset)}">
|
318 | </div>
|
319 | <div class=sub>
|
320 | <div class=name>${asset}</div>
|
321 | ${quantity > 0 ? `<div class=quantity>${quantity}</div>` : ''}
|
322 | </div>
|
323 | `;
|
324 | };
|
325 | const _getServerSrc = name => `\
|
326 | <div class=icon>
|
327 | <img class=img src="${creaturejs.makeStaticCreature('server:' + name)}">
|
328 | </div>
|
329 | <div class=sub>
|
330 | <div class=name>${name}</div>
|
331 | </div>
|
332 | `;
|
333 | const _bindDraggableAsset = (el, asset, dragStart = _nop, dragEnd = _nop) => {
|
334 | el.setAttribute('draggable', true);
|
335 | const _dragStart = e => {
|
336 | e.dataTransfer.setData('text/plain', asset);
|
337 |
|
338 | dragStart();
|
339 | };
|
340 | el.addEventListener('dragstart', _dragStart);
|
341 | const _dragEnd = e => {
|
342 | dragEnd();
|
343 | };
|
344 | el.addEventListener('dragend', _dragEnd);
|
345 |
|
346 | el._unbindDraggable = () => {
|
347 | el.removeAttribute('draggable');
|
348 | el.removeEventListener('dragstart', _dragStart);
|
349 | el.removeEventListener('dragend', _dragEnd);
|
350 | };
|
351 | };
|
352 | const _unbindDraggableAsset = el => {
|
353 | if (el._unbindDraggable) {
|
354 | el._unbindDraggable();
|
355 | el._unbindDraggable = null;
|
356 | }
|
357 | };
|
358 | const _loadInventory = () => {
|
359 | const inventoryGrid = $('#inventory-grid')[0];
|
360 | while (inventoryGrid.hasChildNodes()) {
|
361 | inventoryGrid.removeChild(inventoryGrid.lastChild);
|
362 | }
|
363 | const newBalanceAssets = Object.keys(newBalances);
|
364 | if (newBalanceAssets.length > 0) {
|
365 | const assetEls = newBalanceAssets
|
366 | .map(asset => {
|
367 | const quantity = newBalances[asset];
|
368 |
|
369 | const el = document.createElement('a');
|
370 | el.classList.add('asset');
|
371 | el.innerHTML = _getAssetSrc(asset, quantity);
|
372 | el.addEventListener('click', e => {
|
373 | window.location.href = baseUrl + '/assets/' + asset;
|
374 | });
|
375 | _bindDraggableAsset(el, asset);
|
376 | return el;
|
377 | });
|
378 | for (let i = 0; i < assetEls.length; i++) {
|
379 | const assetEl = assetEls[i];
|
380 | inventoryGrid.appendChild(assetEl);
|
381 | }
|
382 | } else {
|
383 | const el = document.createElement('div');
|
384 | el.classList.add('assets-grid-placeholder');
|
385 | el.innerText = 'Inventory empty :|';
|
386 | inventoryGrid.appendChild(el);
|
387 | }
|
388 |
|
389 | balances = newBalances;
|
390 | };
|
391 | const _loadEquipment = () => {
|
392 | const numEquipments = 4;
|
393 | const equipments = (() => {
|
394 | const s = Cookies.get('data');
|
395 | const j = s && _jsonParse(s);
|
396 | const equipments = j && j.equipment;
|
397 |
|
398 | if (Array.isArray(equipments) && equipments.length === numEquipments && equipments.every(e => e === null || typeof e === 'string')) {
|
399 | return equipments;
|
400 | } else {
|
401 | const result = Array(numEquipments)
|
402 | for (let i = 0; i < numEquipments; i++) {
|
403 | result[i] = null;
|
404 | }
|
405 | return result;
|
406 | }
|
407 | })();
|
408 | const _saveEquipment = () => {
|
409 | let data = _jsonParse(Cookies.get('data'));
|
410 | if (typeof data !== 'object' || data === null) {
|
411 | data = {};
|
412 | }
|
413 | data.equipment = equipments;
|
414 | Cookies.set('data', JSON.stringify(data), {expires: 365});
|
415 | };
|
416 |
|
417 | const equipmentGrid = $('#equipment-grid')[0];
|
418 | while (equipmentGrid.hasChildNodes()) {
|
419 | equipmentGrid.removeChild(equipmentGrid.lastChild);
|
420 | }
|
421 | const assetEls = equipments
|
422 | .map((asset, i) => {
|
423 | if (!(balances[asset] > 0)) {
|
424 | asset = null;
|
425 | }
|
426 |
|
427 | let dropped = false;
|
428 |
|
429 | const el = document.createElement('div');
|
430 | el.classList.add('asset', 'empty');
|
431 | el.innerHTML = `<div class=placeholder>Drag here</div>`;
|
432 | el.addEventListener('mousedown', e => {
|
433 | if (!el.getAttribute('draggable')) {
|
434 | e.preventDefault();
|
435 | }
|
436 | });
|
437 | el.addEventListener('dragover', e => {
|
438 | e.preventDefault();
|
439 | });
|
440 | el.addEventListener('drop', e => {
|
441 | const asset = e.dataTransfer.getData('text/plain');
|
442 |
|
443 | for (let j = 0; j < equipments.length; j++) {
|
444 | if (equipments[j] === asset) {
|
445 | equipments[j] = null;
|
446 | const oldAssetEl = assetEls[j];
|
447 |
|
448 | oldAssetEl.classList.add('empty');
|
449 | oldAssetEl.innerHTML = `<div class=placeholder>Drag here</div>`;
|
450 |
|
451 | _unbindDraggableAsset(oldAssetEl);
|
452 | }
|
453 | }
|
454 |
|
455 | _setAsset(asset);
|
456 |
|
457 | dropped = true;
|
458 | setTimeout(() => {
|
459 | dropped = false;
|
460 | });
|
461 | });
|
462 |
|
463 | const _setAsset = asset => {
|
464 | equipments[i] = asset;
|
465 | _saveEquipment();
|
466 |
|
467 | el.classList.remove('empty');
|
468 | el.innerHTML = _getAssetSrc(asset, 0);
|
469 |
|
470 | _unbindDraggableAsset(el);
|
471 | _bindDraggableAsset(el, asset, _nop, () => {
|
472 | if (!dropped) {
|
473 | equipments[i] = null;
|
474 | _saveEquipment();
|
475 |
|
476 | _unbindDraggableAsset(el);
|
477 |
|
478 | el.classList.add('empty');
|
479 | el.innerHTML = `<div class=placeholder>Drag here</div>`;
|
480 | }
|
481 | });
|
482 | };
|
483 | if (asset !== null) {
|
484 | _setAsset(asset);
|
485 | }
|
486 |
|
487 | return el;
|
488 | });
|
489 | for (let i = 0; i < assetEls.length; i++) {
|
490 | const assetEl = assetEls[i];
|
491 | equipmentGrid.appendChild(assetEl);
|
492 | }
|
493 | };
|
494 | const _loadServers = () => {
|
495 | const servers = (() => {
|
496 | const s = Cookies.get('data');
|
497 | const j = s && _jsonParse(s);
|
498 | const servers = j && j.servers;
|
499 |
|
500 | if (Array.isArray(servers) && servers.every(s => s && s.url && typeof s.url === 'string')) {
|
501 | return servers;
|
502 | } else {
|
503 | return [];
|
504 | }
|
505 | })();
|
506 |
|
507 | const serversGrid = $('#servers-grid')[0];
|
508 | while (serversGrid.hasChildNodes()) {
|
509 | serversGrid.removeChild(serversGrid.lastChild);
|
510 | }
|
511 | const serverEls = servers
|
512 | .map(server => {
|
513 | const {url, name} = server;
|
514 |
|
515 | const el = document.createElement('a');
|
516 | el.classList.add('asset');
|
517 | el.innerHTML = _getServerSrc(name);
|
518 | el.href = url;
|
519 | return el;
|
520 | });
|
521 | for (let i = 0; i < serverEls.length; i++) {
|
522 | const serverEl = serverEls[i];
|
523 | serversGrid.appendChild(serverEl);
|
524 | }
|
525 | $('#servers-wrap')[0].style.display = servers.length > 0 ? null : 'none';
|
526 |
|
527 | balances = newBalances;
|
528 | };
|
529 |
|
530 | _loadInventory();
|
531 | _loadEquipment();
|
532 | _loadServers();
|
533 | })
|
534 | .catch(err => {
|
535 | console.warn(err);
|
536 | });
|
537 | };
|
538 | const _init = () => {
|
539 | const privateKey = Cookies.get('privateKey');
|
540 |
|
541 | return _login(privateKey)
|
542 | .then(() => _loadAsync(privateKey));
|
543 | };
|
544 |
|
545 | const _load = url => {
|
546 | let match = url.match(/^\/id(?:\/(login|assets\/[^\/]+|createAsset|buyAsset|confirmPurchase|keys|mining|monitor))?(?:\?(.*))?$/);
|
547 | const pageMatch = (match && match[1]) || '';
|
548 | const queryString = (match && match[2]) || '';
|
549 |
|
550 | if (pageMatch === 'login') {
|
551 | _setPage('login');
|
552 | } else if (match = pageMatch.match(/^assets\/(.+)$/)) {
|
553 | const asset = match[1];
|
554 | const baseAsset = _getBaseAsset(asset);
|
555 |
|
556 |
|
557 | $('#asset-readme')[0].asset = asset;
|
558 | $('#asset-readme-form')[0].style.display = (balances[baseAsset + ':mint'] > 0) ? null : 'none';
|
559 | $('#mint-asset-form')[0].style.display = (balances[baseAsset + ':mint'] > 0) ? null : 'none';
|
560 | $('#price-asset-form')[0].style.display = (balances[baseAsset + ':mint'] > 0) ? null : 'none';
|
561 |
|
562 | _setPage('asset');
|
563 | } else if (pageMatch === 'createAsset') {
|
564 | _setPage('createAsset');
|
565 | } else if (pageMatch === 'buyAsset') {
|
566 | _setPage('buyAsset');
|
567 | } else if (pageMatch === 'confirmPurchase') {
|
568 | const query = _parseQueryString(queryString);
|
569 | const {asset, price: priceString} = query;
|
570 | const price = parseInt(priceString, 10);
|
571 |
|
572 | if (asset && isFinite(price) && price > 0 && Math.floor(price) === price) {
|
573 | $('#confirm-purchase-asset')[0].innerText = asset;
|
574 | $('#confirm-purchase-price')[0].innerText = price;
|
575 |
|
576 | _setPage('confirmPurchase');
|
577 | } else {
|
578 | console.log('invalid confirm purchase args', query);
|
579 |
|
580 | _setPage('account');
|
581 | }
|
582 | } else if (pageMatch === 'keys') {
|
583 | _setPage('keys');
|
584 | } else if (pageMatch === 'mining') {
|
585 | _setPage('mining');
|
586 | } else if (pageMatch === 'monitor') {
|
587 | _setPage('monitor');
|
588 | } else {
|
589 | _setPage('account');
|
590 | }
|
591 | };
|
592 | const _postMessage = (() => {
|
593 | const target = window.opener || window.parent || window;
|
594 |
|
595 | return m => {
|
596 | target.postMessage(m, '*');
|
597 | };
|
598 | })();
|
599 | const _respond = (error, result) => {
|
600 | const o = {};
|
601 | if (!error) {
|
602 | o.result = result;
|
603 | } else {
|
604 | o.error = error;
|
605 | }
|
606 | _postMessage(o);
|
607 | };
|
608 |
|
609 | |
610 |
|
611 |
|
612 |
|
613 |
|
614 |
|
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
622 |
|
623 |
|
624 |
|
625 |
|
626 |
|
627 |
|
628 | const loginInput = $('#login-input')[0];
|
629 | loginInput.focus();
|
630 | $('#login-generate')[0].addEventListener('click', e => {
|
631 | const privateKey = (() => {
|
632 | const array = new Uint8Array(32);
|
633 | crypto.getRandomValues(array);
|
634 | return array;
|
635 | })();
|
636 | const privateKeyString = base64.encode(privateKey);
|
637 | loginInput.value = privateKeyString;
|
638 | loginInput.focus();
|
639 | loginInput.select();
|
640 | });
|
641 | $('#login-form')[0].addEventListener('submit', e => {
|
642 | e.preventDefault();
|
643 |
|
644 | const privateKey = loginInput.value;
|
645 | if (privateKey) {
|
646 | _login(privateKey);
|
647 |
|
648 | window.location.href = baseUrl + '/';
|
649 | }
|
650 | });
|
651 |
|
652 | $('#create-asset')[0].addEventListener('click', e => {
|
653 | window.location.href = baseUrl + '/createAsset';
|
654 | });
|
655 |
|
656 | $('#buy-asset')[0].addEventListener('click', e => {
|
657 | window.location.href = baseUrl + '/buyAsset';
|
658 | });
|
659 |
|
660 | |
661 |
|
662 |
|
663 | $('#blocks-form')[0].addEventListener('submit', e => {
|
664 | e.preventDefault();
|
665 |
|
666 | const blocksData = $('#blocks-data')[0];
|
667 | blocksData.innerText = '';
|
668 |
|
669 | frontendApi.requestBlockCache()
|
670 | .then(blocks => {
|
671 | blocksData.innerText = JSON.stringify(blocks, null, 2);
|
672 | })
|
673 | .catch(err => {
|
674 | console.warn(err);
|
675 | });
|
676 | });
|
677 | $('#mempool-form')[0].addEventListener('submit', e => {
|
678 | e.preventDefault();
|
679 |
|
680 | const mempoolData = $('#mempool-data')[0];
|
681 | mempoolData.innerText = '';
|
682 |
|
683 | frontendApi.requestMempool()
|
684 | .then(mempool=> {
|
685 | mempoolData.innerText = JSON.stringify(mempool, null, 2);
|
686 | })
|
687 | .catch(err => {
|
688 | console.warn(err);
|
689 | });
|
690 | });
|
691 |
|
692 | const showSecrets = $('#show-secrets')[0];
|
693 | const hideSecrets = $('#hide-secrets')[0];
|
694 | const secretValue = $('#secret-value')[0];
|
695 | showSecrets.addEventListener('click', e => {
|
696 | secretValue.classList.add('visible');
|
697 | showSecrets.style.display = 'none';
|
698 | hideSecrets.style.display = 'block';
|
699 | });
|
700 | hideSecrets.addEventListener('click', e => {
|
701 | secretValue.classList.remove('visible');
|
702 | showSecrets.style.display = 'block';
|
703 | hideSecrets.style.display = 'none';
|
704 | });
|
705 |
|
706 | const miningForm = $('#mining-form')[0];
|
707 | miningForm.addEventListener('submit', e => {
|
708 | if (!miningForm.classList.contains('running')) {
|
709 | fetch(baseUrl + '/api/startmine', {
|
710 | method: 'POST',
|
711 | credentials: 'include',
|
712 | })
|
713 | .then(_resBlob)
|
714 | .then(() => {
|
715 | miningForm.classList.add('running');
|
716 | $('#mining-status')[0].innerText = 'Running...';
|
717 | })
|
718 | .catch(err => {
|
719 | console.warn(err);
|
720 |
|
721 | header.setNotification(false, 'Failed to start mining');
|
722 | });
|
723 | } else {
|
724 | fetch(baseUrl + '/api/stopmine', {
|
725 | method: 'POST',
|
726 | credentials: 'include',
|
727 | })
|
728 | .then(_resBlob)
|
729 | .then(() => {
|
730 | miningForm.classList.remove('running');
|
731 | $('#mining-status')[0].innerText = 'NOT running!';
|
732 | })
|
733 | .catch(err => {
|
734 | console.warn(err);
|
735 |
|
736 | header.setNotification(false, 'Failed to stop mining');
|
737 | });
|
738 | }
|
739 |
|
740 | e.preventDefault();
|
741 | });
|
742 |
|
743 | $('#asset-readme-edit-button')[0].addEventListener('click', e => {
|
744 | $('#asset-readme-form')[0].classList.add('editing');
|
745 |
|
746 | $('#asset-readme-input')[0].value = $('#asset-readme')[0].text;
|
747 | });
|
748 | $('#asset-readme-form')[0].addEventListener('submit', e => {
|
749 | $('#asset-readme-form')[0].classList.remove('editing');
|
750 |
|
751 | const assetReadmeInput = $('#asset-readme-input')[0];
|
752 | const readme = assetReadmeInput.value;
|
753 | assetReadmeInput.value = '';
|
754 |
|
755 | _setReadmeText(readme);
|
756 |
|
757 | fetch(baseUrl + '/api/data/' + _getBaseAsset($('#asset-readme')[0].asset), {
|
758 | method: 'PUT',
|
759 | headers: (() => {
|
760 | const headers = new Headers();
|
761 | headers.append('Content-Type', 'text/plain');
|
762 | return headers;
|
763 | })(),
|
764 | body: readme,
|
765 | credentials: 'include',
|
766 | })
|
767 | .then(_resBlob)
|
768 | .then(() => {
|
769 | header.setNotification(true, 'Updated asset readme');
|
770 | })
|
771 | .catch(err => {
|
772 | console.warn(err);
|
773 |
|
774 | header.setNotification(false, 'Failed to update asset readme');
|
775 | });
|
776 |
|
777 | e.preventDefault();
|
778 | });
|
779 | $('#asset-readme-cancel-button')[0].addEventListener('click', e => {
|
780 | $('#asset-readme-form')[0].classList.remove('editing');
|
781 |
|
782 | _setReadmeText($('#asset-readme')[0].text);
|
783 | });
|
784 | $('#send-asset-form')[0].addEventListener('submit', e => {
|
785 | e.preventDefault();
|
786 |
|
787 | const dstAddress = $('#send-asset-address-input')[0].value;
|
788 | const quantity = parseInt($('#send-asset-quantity-input')[0].value, 10);
|
789 |
|
790 | if (dstAddress && !isNaN(quantity)) {
|
791 | const {privateKey} = JSON.parse(secretValue.innerText);
|
792 | const srcAddress = frontendApi.getAddress(base64.decode(privateKey));
|
793 | const {asset} = $('#asset-readme')[0];
|
794 |
|
795 | fetch(baseUrl + '/api/send', {
|
796 | method: 'POST',
|
797 | headers: (() => {
|
798 | const headers = new Headers();
|
799 | headers.append('Content-Type', 'application/json');
|
800 | return headers;
|
801 | })(),
|
802 | body: JSON.stringify({
|
803 | asset,
|
804 | quantity,
|
805 | srcAddress,
|
806 | dstAddress,
|
807 | privateKey,
|
808 | }),
|
809 | })
|
810 | .then(_resJson)
|
811 | .then(result => {
|
812 | header.setNotification(true, 'Send asset message submitted to network');
|
813 | })
|
814 | .catch(err => {
|
815 | console.warn(err);
|
816 |
|
817 | header.setNotification(false, 'Failed to submit asset message to network');
|
818 | });
|
819 | } else {
|
820 | header.setNotification(false, 'Form is incomplete');
|
821 | }
|
822 | });
|
823 | $('#mint-asset-form')[0].addEventListener('submit', e => {
|
824 | e.preventDefault();
|
825 |
|
826 | const quantity = parseInt($('#mint-quantity-input')[0].value, 10);
|
827 |
|
828 | if (!isNaN(quantity)) {
|
829 | const baseAsset = $('#asset-readme')[0].asset.replace(/:.*$/, '');
|
830 | const subasset = $('#mint-subasset-name-input')[0].value;
|
831 | const asset = baseAsset + (subasset ? ('.' + subasset) : '');
|
832 | const {privateKey} = JSON.parse(secretValue.innerText);
|
833 |
|
834 | fetch(baseUrl + '/api/mint', {
|
835 | method: 'POST',
|
836 | headers: (() => {
|
837 | const headers = new Headers();
|
838 | headers.append('Content-Type', 'application/json');
|
839 | return headers;
|
840 | })(),
|
841 | body: JSON.stringify({
|
842 | asset,
|
843 | quantity,
|
844 | privateKey,
|
845 | }),
|
846 | })
|
847 | .then(_resJson)
|
848 | .then(result => {
|
849 | header.setNotification(true, 'Mint asset message submitted to network');
|
850 | })
|
851 | .catch(err => {
|
852 | console.warn(err);
|
853 |
|
854 | header.setNotification(false, 'Failed to submit mint asset message to network');
|
855 | });
|
856 | } else {
|
857 | header.setNotification(false, 'Form is incomplete');
|
858 | }
|
859 | });
|
860 | const mintSubassetNameInput = $('#mint-subasset-name-input')[0];
|
861 | mintSubassetNameInput.addEventListener('blur', e => {
|
862 | mintSubassetNameInput.value = mintSubassetNameInput.value.toUpperCase().replace(/[^a-z0-9]/ig, '');
|
863 | });
|
864 |
|
865 | $('#price-asset-form')[0].addEventListener('submit', e => {
|
866 | e.preventDefault();
|
867 |
|
868 | const price = parseInt($('#price-asset-input')[0].value, 10);
|
869 |
|
870 | if (!isNaN(price)) {
|
871 | const asset = $('#asset-readme')[0].asset.replace(/:.*$/, '');
|
872 | const {privateKey} = JSON.parse(secretValue.innerText);
|
873 |
|
874 | fetch(baseUrl + '/api/price', {
|
875 | method: 'POST',
|
876 | headers: (() => {
|
877 | const headers = new Headers();
|
878 | headers.append('Content-Type', 'application/json');
|
879 | return headers;
|
880 | })(),
|
881 | body: JSON.stringify({
|
882 | asset,
|
883 | price,
|
884 | privateKey,
|
885 | }),
|
886 | })
|
887 | .then(_resJson)
|
888 | .then(result => {
|
889 | header.setNotification(true, 'Price asset message submitted to network');
|
890 | })
|
891 | .catch(err => {
|
892 | console.warn(err);
|
893 |
|
894 | header.setNotification(false, 'Error submitting price asset message to network');
|
895 | });
|
896 | }
|
897 | });
|
898 |
|
899 | $('#create-asset-form')[0].addEventListener('submit', e => {
|
900 | e.preventDefault();
|
901 |
|
902 | const asset = $('#create-asset-name-input')[0].value;
|
903 |
|
904 | if (asset) {
|
905 | const {privateKey} = JSON.parse(secretValue.innerText);
|
906 |
|
907 | fetch(baseUrl + '/api/minter', {
|
908 | method: 'POST',
|
909 | headers: (() => {
|
910 | const headers = new Headers();
|
911 | headers.append('Content-Type', 'application/json');
|
912 | return headers;
|
913 | })(),
|
914 | body: JSON.stringify({
|
915 | asset,
|
916 | privateKey,
|
917 | }),
|
918 | })
|
919 | .then(_resJson)
|
920 | .then(result => {
|
921 | header.setNotification(true, 'Create asset message submitted to network');
|
922 | })
|
923 | .catch(err => {
|
924 | console.warn(err);
|
925 |
|
926 | header.setNotification(false, 'Error submitting create asset message to network');
|
927 | });
|
928 | }
|
929 | });
|
930 | const createAssetNameInput = $('#create-asset-name-input')[0];
|
931 | createAssetNameInput.addEventListener('blur', e => {
|
932 | createAssetNameInput.value = createAssetNameInput.value.toUpperCase().replace(/[^a-z0-9]/ig, '');
|
933 | });
|
934 |
|
935 | $('#buy-asset-form')[0].addEventListener('submit', e => {
|
936 | e.preventDefault();
|
937 |
|
938 | const asset = $('#buy-asset-name-input')[0].value;
|
939 | const quantity = parseInt($('#buy-asset-quantity-input')[0].value, 10);
|
940 |
|
941 | if (asset && !isNaN(quantity)) {
|
942 | fetch(`${baseUrl}/api/unconfirmedPrice/${asset}`)
|
943 | .then(_resJson)
|
944 | .then(price => {
|
945 | if (isFinite(price) && price > 0) {
|
946 | const {privateKey} = JSON.parse(secretValue.innerText);
|
947 | return fetch(baseUrl + '/api/buy', {
|
948 | method: 'POST',
|
949 | headers: (() => {
|
950 | const headers = new Headers();
|
951 | headers.append('Content-Type', 'application/json');
|
952 | return headers;
|
953 | })(),
|
954 | body: JSON.stringify({
|
955 | asset,
|
956 | quantity,
|
957 | price,
|
958 | privateKey,
|
959 | }),
|
960 | })
|
961 | .then(_resJson);
|
962 | } else {
|
963 | const err = new Error('asset is not for sale');
|
964 | err.code = 'ENOSALE';
|
965 | return Promise.reject(err);
|
966 | }
|
967 | })
|
968 | .then(result => {
|
969 | header.setNotification(true, 'Buy asset message submitted to network');
|
970 | })
|
971 | .catch(err => {
|
972 | console.warn(err);
|
973 |
|
974 | if (err.code === 'ENOSALE') {
|
975 | header.setNotification(false, 'Asset is not for sale');
|
976 | } else {
|
977 | header.setNotification(false, 'Failed to submit buy asset message to network');
|
978 | }
|
979 | });
|
980 | } else {
|
981 | header.setNotification(false, 'Form is incomplete');
|
982 | }
|
983 | });
|
984 | const buyAssetNameInput = $('#buy-asset-name-input')[0];
|
985 | buyAssetNameInput.addEventListener('blur', e => {
|
986 | buyAssetNameInput.value = buyAssetNameInput.value.toUpperCase().replace(/[^a-z0-9]/ig, '');
|
987 | });
|
988 |
|
989 | $('#confirm-purchase-form')[0].addEventListener('submit', e => {
|
990 | e.preventDefault();
|
991 |
|
992 | const asset = $('#confirm-purchase-asset')[0].value;
|
993 | const quantity = 1;
|
994 | const price = parseInt($('#confirm-purchase-price')[0].value, 10);
|
995 |
|
996 | fetch(baseUrl + '/api/buy', {
|
997 | method: 'POST',
|
998 | headers: (() => {
|
999 | const headers = new Headers();
|
1000 | headers.append('Content-Type', 'application/json');
|
1001 | return headers;
|
1002 | })(),
|
1003 | body: JSON.stringify({
|
1004 | asset,
|
1005 | quantity,
|
1006 | price,
|
1007 | privateKey,
|
1008 | }),
|
1009 | })
|
1010 | .then(_resJson)
|
1011 | .then(result => {
|
1012 | header.setNotification(true, 'Buy asset message submitted to network');
|
1013 | })
|
1014 | .catch(err => {
|
1015 | console.warn(err);
|
1016 |
|
1017 | header.setNotification(false, 'Failed to submit buy asset message to network');
|
1018 | });
|
1019 | });
|
1020 |
|
1021 | const _jsonParse = s => {
|
1022 | try {
|
1023 | return JSON.parse(s);
|
1024 | } catch(err) {
|
1025 | return undefined;
|
1026 | }
|
1027 | };
|
1028 | const _parseQueryString = s => {
|
1029 | const result = {};
|
1030 | const vars = s.split('&');
|
1031 |
|
1032 | for (let i = 0; i < vars.length; i++) {
|
1033 | const pair = vars[i].split('=');
|
1034 |
|
1035 | result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
|
1036 | }
|
1037 |
|
1038 | return result;
|
1039 | };
|
1040 |
|
1041 | window.addEventListener('popstate', () => {
|
1042 | _load(document.location.href.replace(/^[a-z]+:\/\/[^\/]+/, ''));
|
1043 | });
|
1044 |
|
1045 | _init()
|
1046 | .then(() => _load(document.location.href.replace(/^[a-z]+:\/\/[^\/]+/, '')));
|
1047 | };
|
1048 | _boot();
|