UNPKG

32.4 kBJavaScriptView Raw
1window.module = {};
2
3const Cookies = require('js-cookie');
4const frontendApi = require('./frontend-api.js')({crdsUrl: '/id/api'});
5const base64 = require('./base64');
6
7window.$ = s => document.querySelectorAll(s);
8
9const baseUrl = '/id';
10const pages = {
11 login: () => {
12 // const statusbarEl = $('.statusbar.login')[0];
13 // statusbarEl.style.display = 'flex';
14 const pageEl = $('#login-page')[0];
15 pageEl.style.display = null;
16
17 return () => {
18 // statusbarEl.style.display = 'none';
19 pageEl.style.display = 'none';
20
21 $('#login-input')[0].value = '';
22 };
23 },
24 account: () => {
25 // const statusbarEl = $('.statusbar.account')[0];
26 // statusbarEl.style.display = 'flex';
27 header.setTab('avatar');
28 const pageEl = $('#account-page')[0];
29 pageEl.style.display = null;
30
31 return () => {
32 // statusbarEl.style.display = 'none';
33 pageEl.style.display = 'none';
34 };
35 },
36 asset: () => {
37 // const statusbarEl = $('.statusbar.asset')[0];
38 // statusbarEl.style.display = 'flex';
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 // statusbarEl.style.display = 'none';
55 pageEl.style.display = 'none';
56
57 // $('#asset-assetname')[0].innerText = '';
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 // const statusbarEl = $('.statusbar.create-asset')[0];
69 // statusbarEl.style.display = 'flex';
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 // statusbarEl.style.display = 'none';
81 pageEl.style.display = 'none';
82
83 $('#create-asset-name-input')[0].value = '';
84 };
85 },
86 buyAsset: () => {
87 // const statusbarEl = $('.statusbar.buy-asset')[0];
88 // statusbarEl.style.display = 'flex';
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 // statusbarEl.style.display = 'none';
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 // const statusbarEl = $('.statusbar.confirm-purchase')[0];
108 // statusbarEl.style.display = 'flex';
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 // const statusbarEl = $('.statusbar.monitor')[0];
123 // statusbarEl.style.display = 'flex';
124 header.setTab('keys');
125 const pageEl = $('#keys-page')[0];
126 pageEl.style.display = null;
127
128 return () => {
129 // statusbarEl.style.display = 'none';
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 // const statusbarEl = $('.statusbar.monitor')[0];
142 // statusbarEl.style.display = 'flex';
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 // statusbarEl.style.display = 'none';
158 pageEl.style.display = 'none';
159 };
160 },
161 monitor: () => {
162 // const statusbarEl = $('.statusbar.monitor')[0];
163 // statusbarEl.style.display = 'flex';
164 header.setTab('monitor');
165 const pageEl = $('#monitor-page')[0];
166 pageEl.style.display = null;
167
168 return () => {
169 // statusbarEl.style.display = 'none';
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
187const _getBaseAsset = asset => {
188 const match = asset.match(/^(.+):mint$/);
189 return match ? match[1] : asset;
190};
191const _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};
208const _nop = () => {};
209const _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};
219const _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};
231const _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/* const loginUsername = $('#login-username')[0];
243const accountUsername = $('#account-username')[0];
244const assetUsername = $('#asset-username')[0];
245const assetAssetname = $('#asset-assetname')[0];
246const createAssetUsername = $('#create-asset-username')[0];
247const buyAssetUsername = $('#buy-asset-username')[0];
248const confirmPurchaseUsername = $('#confirm-purchase-username')[0];
249const monitorUsername = $('#monitor-username')[0]; */
250
251const _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 // document.location.href = baseUrl + '/logout';
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 /* loginUsername.innerText = address;
281 accountUsername.innerText = address;
282 assetUsername.innerText = address;
283 createAssetUsername.innerText = address;
284 buyAssetUsername.innerText = address;
285 confirmPurchaseUsername.innerText = address;
286 monitorUsername.innerText = address;
287
288 const src = creaturejs.makeStaticCreature('user:' + address);
289 [
290 $('#login-icon')[0],
291 $('#account-icon')[0],
292 $('#asset-icon')[0],
293 $('#create-asset-icon')[0],
294 $('#buy-asset-icon')[0],
295 $('#confirm-purchase-icon')[0],
296 $('#monitor-icon')[0],
297 ].forEach(icon => {
298 icon.src = src;
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; // prevent dragend from synchronously clearing this element
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 // $('#asset-assetname')[0].innerText = asset;
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 $('#login-login-button')[0],
611 $('#account-login-button')[0],
612 $('#asset-login-button')[0],
613 $('#create-asset-login-button')[0],
614 $('#buy-asset-login-button')[0],
615 $('#confirm-purchase-login-button')[0],
616 $('#monitor-login-button')[0],
617 ].forEach(loginButton => {
618 loginButton.addEventListener('click', e => {
619 _navigate('/login');
620 });
621 });
622 [loginUsername, assetUsername, createAssetUsername, buyAssetUsername, confirmPurchaseUsername, monitorUsername].forEach(username => {
623 username.addEventListener('click', e => {
624 _navigate('');
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 /* $('#monitor')[0].addEventListener('click', e => {
661 _navigate('/monitor');
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();