-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
259 lines (222 loc) · 13.4 KB
/
index.html
File metadata and controls
259 lines (222 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Sorteador UFSCar</title>
<meta name="description" content="Ferramenta para sorteio auditável de números inteiros e de sequências">
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-sha3/0.9.3/sha3.min.js" integrity="sha512-l8ZGwlcmmNhyMXRweX0SqrZHIdfK3UgOSJsdSK5ozqlXOsZDogZFYp+TUzzI7pFYGUmzTCKZjS1q0nHiWOvs0g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js" integrity="sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.slim.min.js" integrity="sha512-sNylduh9fqpYUK5OYXWcBleGzbZInWj8yCJAU57r1dpSK9tP2ghf/SRYCMj+KsslFkCOt3TvJrX2AV/Gc3wOqA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@eonasdan/tempus-dominus@6.9.11/dist/css/tempus-dominus.min.css" integrity="sha512-f3w85ZAdifaAeq4BNzWbSC9NJ9W4BwXdKXz53bGmdgKso5pP0LrDWLLYdAeq/Sv3SKiUGV3+Zeik2Un/g2v/jw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js" integrity="sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/js/solid.min.js" integrity="sha512-H/FYzgm63CLJLwSgCNv7zmAHWnbw7GwOrnCjE15CD969yHWj7fGDiHHLZuZJLO9ZGIkBR/JL91/p/ddbtUUgQQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/js/brands.min.js" integrity="sha512-ILOQokRQNF0S8SKV6fnaLNj02CmZnDWmYUr3zlH4jwToep0lWc7twuTzF+Mm0cKPdszi0xe8KymVi2y7mAweVQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/js/fontawesome.min.js" integrity="sha512-j12pXc2gXZL/JZw5Mhi6LC7lkiXL0e2h+9ZWpqhniz0DkDrO01VNlBrG3LkPBn6DgG2b8CDjzJT+lxfocsS1Vw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/@eonasdan/tempus-dominus@6.9.11/dist/js/tempus-dominus.min.js" integrity="sha512-g2sBCPwYBvzR1GxdGGa4lboSBnVkEYrGuHHKsUDDTosbQovBsOKLeaT2E7H0a4gtxkEx4V+Kjnnu5hVufhWb1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="importmap">
{
"integrity": {
"./drand-client/index.mjs": "sha512-Vy47UDKyJTLvGpT2xG1zA8+N19xLSHVrd1FWv7nvtZ584U3pjoq5X3UCVhGbxlbSuUsEkOgitE65vjUlMaJqHg=="
}
}
</script>
<script type="module">
import {
HttpChainClient,
HttpCachingChain,
watch,
fetchBeacon,
roundAt
} from './drand-client/index.mjs'
const chainHash = '8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce' // (hex encoded)
const publicKey = '868f005eb8e6e4ca0a47c8a77ceaa5309a47978a7c71bc5cce96366b5d7a569937c529eeda66c7293784a9402801af31' // (hex encoded)
const options = {
disableBeaconVerification: false, // `true` disables checking of signatures on beacons - faster but insecure!!!
noCache: false, // `true` disables caching when retrieving beacons for some providers
chainVerificationParams: { chainHash, publicKey } // these are optional, but recommended! They are compared for parity against the `/info` output of a given node
};
const chain = new HttpCachingChain('https://api.drand.sh', options);
const client = new HttpChainClient(chain, options);
let datetimepicker1;
async function f_config_submit(event) {
const op = event.originalEvent.submitter.value;
const edital = $('#edital').val();
const qtd = parseInt($('#qtd').val());
const maxval = parseInt($('#maxval').val());
const time = timeRoundToMin(datetimepicker1.viewDate.getTime());
if (qtd > maxval) {
alert('A quantidade de valores sorteados deve ser menor ou igual ao valor máximo.');
return;
}
gui_wait(op);
const info = await client.chain().info();
const roundNumber = roundAt(time, info);
let beacon;
if (op === 'sortear') {
let b = await client.latest();
if (Math.abs(b.round - roundAt(Date.now(), info)) > 1) {
gui_idle();
alert('O relógio do seu computador está errado. Habilite a sincronização de relógio do seu sistema operacional com a rede e tente novamente.');
return;
}
if (b.round >= roundNumber) {
gui_idle();
alert('O horário solicitado já passou. Escolha um horário no futuro.');
return;
}
const abortController = new AbortController();
for await (let b of watch(client, abortController)) {
if (b.round === roundNumber) {
beacon = b;
abortController.abort();
}
else if (b.round > roundNumber) {
gui_idle();
alert('O horário solicitado já passou. Escolha um horário no futuro.');
abortController.abort();
return;
}
}
}
else if (op === 'auditar') {
try {
beacon = await fetchBeacon(client, roundNumber);
}
catch (err) {
gui_idle();
alert('Não é possível auditar um sorteio que ainda não aconteceu. Escolha um horário no passado.');
return;
}
}
gui_show_results();
async function add_log(msg) {
$('#log').append(document.createTextNode(`${msg}\n`));
await new Promise(r => setTimeout(r, 0)); // allow DOM to update
}
await add_log(`round = ${beacon.round}`);
const randomness = beacon.randomness;
let sample_num = 0;
async function sample(n) {
const mask = (1n << BigInt(Math.ceil(Math.log2(n)))) - 1n;
const maskhex = `0x${mask.toString(16)}`;
let res;
while (true) {
const input = `${edital}|${randomness}|${sample_num}`;
sample_num++;
const hash = BigInt('0x' + sha3_224(input));
res = Number(hash & mask) + 1;
await add_log(`(sha3_224('${input}') & ${maskhex}) + 1 = ${res}`);
if (res <= n) {
break;
}
}
return res;
}
if (qtd === 1) {
$('#result').html(await sample(maxval));
}
else if (qtd > 1) {
let res = [];
let rem = new Set(Array.from({length: maxval}, (_, i) => i + 1));
for (let i = 1; i <= qtd; i++) {
await add_log('');
await add_log(`* sorteio da ${i}ª colocação`);
if (rem.size === 1) {
res.push(rem.values().next().value);
}
else {
while (true) {
const elem = await sample(maxval);
if (rem.has(elem)) {
rem.delete(elem);
res.push(elem);
break;
}
}
}
await add_log(`resultado parcial: ${res}`);
}
$('#result').html(res.join(',<br />'));
}
$('#result_title').html(op === 'sortear' ? 'Resultado do sorteio' : 'Resultado da auditoria');
}
$(() => {
datetimepicker1 = new tempusDominus.TempusDominus(document.getElementById('datetimepicker1'), {
localization: {
locale: 'pt-BR',
format: 'dd/MM/yyyy HH:mm',
}
});
datetimepicker1.dates.setValue(tempusDominus.DateTime.convert(new Date(timeRoundToMin(Date.now()) + 2 * 60000)));
$('#f_config').submit((ev) => { ev.preventDefault(); f_config_submit(ev); });
gui_idle();
});
function timeRoundToMin(time) {
return time - (time % 60000);
}
function gui_idle() {
$('#btn_sortear').html('Sortear');
$('#btn_auditar').html('Auditar');
$('input, button').prop('disabled', false);
}
function gui_wait(op) {
const spinner = '<i class="fas fa-spinner fa-spin"></i>';
if (op === 'sortear') {
$('#btn_sortear').html(spinner);
}
else if (op === 'auditar') {
$('#btn_auditar').html(spinner);
}
$('input, button').prop('disabled', true);
}
function gui_show_results() {
$('button').addClass('d-none');
$('#s_results').removeClass('d-none');
}
</script>
</head>
<body>
<main class="container-sm">
<div id="s_config" class="bg-light rounded p-2">
<h1>Configurações do sorteio</h1>
<form id="f_config">
<div class="mb-3">
<label for="edital">Identificação do edital</label>
<input type="text" id="edital" class="form-control" placeholder="permitidos a-zA-Z0-9./-" pattern="[a-zA-Z0-9.\/\-]+" required autofocus>
</div>
<div class="mb-3">
<label for="datetimepicker1Input">Horário do sorteio</label>
<div class="input-group" id="datetimepicker1" data-td-target-input="nearest" data-td-target-toggle="nearest">
<input id="datetimepicker1Input" type="text" class="form-control" data-td-target="#datetimepicker1" />
<span class="input-group-text" data-td-target="#datetimepicker1" data-td-toggle="datetimepicker">
<span class="fas fa-calendar"></span>
</span>
</div>
</div>
<div class="mb-3">
<div class="d-inline-flex align-items-center">
<label for="qtd" style="margin-right:6pt">Sortear</label>
<input type="number" id="qtd" class="form-control" style="max-width:90px" min="1" value="1" required>
<label for="qtd" style="margin-left:6pt;margin-right:6pt">valores entre 1 e</label>
<input type="number" id="maxval" class="form-control" style="max-width:90px" min="2" required>
</div>
</div>
<button id="btn_sortear" class="btn btn-lg btn-primary btn-block mt-2" type="submit" value="sortear">Sortear</button>
<button id="btn_auditar" class="btn btn-lg btn-secondary btn-block mt-2" type="submit" value="auditar">Auditar</button>
</form>
</div>
<div id="s_results" class="mt-4 d-none">
<div class="bg-light rounded p-2 mb-4">
<h2 class="h1" id="result_title">Resultado</h2>
<p id="result" class="display-2 user-select-all"></p>
</div>
<pre id="log" class="user-select-all"></pre>
</div>
<div class="fw-light mt-3" style="font-size:.9rem">
Usando dados de <a href="https://drand.love">drand.love</a>. <a href="https://github.com/ufscar/sorteador"><i class="fa fa-brands fa-github"></i></a>
</div>
</main>
</body>
</html>