"use strict"; function goto_up2k() { if (up2k === false) return goto('bup'); if (!up2k) return setTimeout(goto_up2k, 100); up2k.init_deps(); } // chrome requires https to use crypto.subtle, // usually it's undefined but some chromes throw on invoke var up2k = null, sha_js = window.WebAssembly ? 'hw' : 'ac', // ff53,c57,sa11 m = 'will use ' + sha_js + ' instead of native sha512 due to'; try { var cf = crypto.subtle || crypto.webkitSubtle; cf.digest('SHA-512', new Uint8Array(1)).then( function (x) { console.log('sha-ok'); up2k = up2k_init(cf); }, function (x) { console.log(m, x); up2k = up2k_init(false); } ); } catch (ex) { console.log(m, ex); try { up2k = up2k_init(false); } catch (ex) { } } treectl.onscroll(); function up2k_flagbus() { var flag = { "id": Math.floor(Math.random() * 1024 * 1024 * 1023 * 2), "ch": new BroadcastChannel("up2k_flagbus"), "ours": false, "owner": null, "wants": null, "act": false, "last_tx": ["x", null] }; var dbg = function (who, msg) { console.log('flagbus(' + flag.id + '): [' + who + '] ' + msg); }; flag.ch.onmessage = function (e) { var who = e.data[0], what = e.data[1]; if (who == flag.id) { dbg(who, 'hi me (??)'); return; } flag.act = Date.now(); if (what == "want") { // lowest id wins, don't care if that's us if (who < flag.id) { dbg(who, 'wants (ack)'); flag.wants = [who, flag.act]; } else { dbg(who, 'wants (ign)'); } } else if (what == "have") { dbg(who, 'have'); flag.owner = [who, flag.act]; } else if (what == "give") { if (flag.owner && flag.owner[0] == who) { flag.owner = null; dbg(who, 'give (ok)'); } else { dbg(who, 'give, INVALID, ' + flag.owner); } } else if (what == "hi") { dbg(who, 'hi'); flag.ch.postMessage([flag.id, "hey"]); } else { dbg('?', e.data); } }; var tx = function (now, msg) { var td = now - flag.last_tx[1]; if (td > 500 || flag.last_tx[0] != msg) { dbg('*', 'tx ' + msg); flag.ch.postMessage([flag.id, msg]); flag.last_tx = [msg, now]; } }; var do_take = function (now) { tx(now, "have"); flag.owner = [flag.id, now]; flag.ours = true; }; var do_want = function (now) { tx(now, "want"); }; flag.take = function (now) { if (flag.ours) { do_take(now); return; } if (flag.owner && now - flag.owner[1] > 5000) { flag.owner = null; } if (flag.wants && now - flag.wants[1] > 5000) { flag.wants = null; } if (!flag.owner && !flag.wants) { do_take(now); return; } do_want(now); }; flag.give = function () { dbg('#', 'put give'); flag.ch.postMessage([flag.id, "give"]); flag.owner = null; flag.ours = false; }; flag.ch.postMessage([flag.id, 'hi']); return flag; } function U2pvis(act, btns) { var r = this; r.act = act; r.ctr = { "ok": 0, "ng": 0, "bz": 0, "q": 0 }; r.tab = []; r.head = 0; r.tail = -1; r.wsz = 3; var markup = { '404': '404', 'ERROR': 'ERROR', 'OS-error': 'OS-error', 'found': 'found', 'YOLO': 'YOLO', 'done': 'done', }; r.addfile = function (entry, sz, draw) { r.tab.push({ "hn": entry[0], "ht": entry[1], "hp": entry[2], "in": 'q', "nh": 0, //hashed "nd": 0, //done "cb": [], // bytes done in chunk "bt": sz, // bytes total "bd": 0, // bytes done "bd0": 0 // upload start }); r.ctr["q"]++; if (!draw) return; r.drawcard("q"); if (r.act == "q") { r.addrow(r.tab.length - 1); } if (r.act == "bz") { r.bzw(); } }; r.is_act = function (card) { if (r.act == "done") return card == "ok" || card == "ng"; return r.act == card; } r.seth = function (nfile, field, html) { var fo = r.tab[nfile]; field = ['hn', 'ht', 'hp'][field]; if (fo[field] === html) return; fo[field] = html; if (!r.is_act(fo.in)) return; var obj = ebi('f{0}{1}'.format(nfile, field.slice(1))); obj.innerHTML = field == 'ht' ? (markup[html] || html) : html; if (field == 'hp') { obj.style.color = ''; obj.style.background = ''; } }; r.setab = function (nfile, nblocks) { var t = []; for (var a = 0; a < nblocks; a++) t.push(0); r.tab[nfile].cb = t; }; r.setat = function (nfile, blocktab) { r.tab[nfile].cb = blocktab; var bd = 0; for (var a = 0; a < blocktab.length; a++) bd += blocktab[a]; r.tab[nfile].bd = bd; r.tab[nfile].bd0 = bd; }; r.perc = function (bd, bd0, sz, t0) { var td = Date.now() - t0, p = bd * 100.0 / sz, nb = bd - bd0, spd = nb / (td / 1000), eta = (sz - bd) / spd; return [p, s2ms(eta), spd / (1024 * 1024)]; }; r.hashed = function (fobj) { var fo = r.tab[fobj.n], nb = fo.bt * (++fo.nh / fo.cb.length), p = r.perc(nb, 0, fobj.size, fobj.t_hashing); fo.hp = '{0}%, {1}, {2} MB/s'.format( p[0].toFixed(2), p[1], p[2].toFixed(2) ); if (!r.is_act(fo.in)) return; var obj = ebi('f{0}p'.format(fobj.n)), o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; obj.innerHTML = fo.hp; obj.style.color = '#fff'; obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #09d ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)'; }; r.prog = function (fobj, nchunk, cbd) { var fo = r.tab[fobj.n], delta = cbd - fo.cb[nchunk]; fo.cb[nchunk] = cbd; fo.bd += delta; var p = r.perc(fo.bd, fo.bd0, fo.bt, fobj.t_uploading); fo.hp = '{0}%, {1}, {2} MB/s'.format( p[0].toFixed(2), p[1], p[2].toFixed(2) ); if (!r.is_act(fo.in)) return; var obj = ebi('f{0}p'.format(fobj.n)), o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; if (!obj) { var msg = [ "act", r.act, "in", fo.in, "is_act", r.is_act(fo.in), "head", r.head, "tail", r.tail, "nfile", fobj.n, "name", fobj.name, "sz", fobj.size, "bytesDelta", delta, "bytesDone", fo.bd, ], m2 = '', ds = QSA("#u2tab>tbody>tr>td:first-child>a:last-child"); for (var a = 0; a < msg.length; a += 2) m2 += msg[a] + '=' + msg[a + 1] + ', '; console.log(m2); for (var a = 0, aa = ds.length; a < aa; a++) { var id = ds[a].parentNode.getAttribute('id').slice(1, -1); console.log("dom %d/%d = [%s] in(%s) is_act(%s) %s", a, aa, id, r.tab[id].in, r.is_act(fo.in), ds[a].textContent); } for (var a = 0, aa = r.tab.length; a < aa; a++) if (r.is_act(r.tab[a].in)) console.log("tab %d/%d = sz %s", a, aa, r.tab[a].bt); throw new Error('see console'); } obj.innerHTML = fo.hp; obj.style.color = '#fff'; obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)'; }; r.move = function (nfile, newcat) { var fo = r.tab[nfile], oldcat = fo.in, bz_act = r.act == "bz"; if (oldcat == newcat) return; fo.in = newcat; r.ctr[oldcat]--; r.ctr[newcat]++; r.drawcard(oldcat); r.drawcard(newcat); if (r.is_act(newcat)) { r.tail = Math.max(r.tail, nfile + 1); if (!ebi('f' + nfile)) r.addrow(nfile); } else if (r.is_act(oldcat)) { while (r.head < Math.min(r.tab.length, r.tail) && r.precard[r.tab[r.head].in]) r.head++; if (!bz_act) { var tr = ebi("f" + nfile); tr.parentNode.removeChild(tr); } } else return; if (bz_act) r.bzw(); }; r.bzw = function () { var first = QS('#u2tab>tbody>tr:first-child'); if (!first) return; var last = QS('#u2tab>tbody>tr:last-child'); first = parseInt(first.getAttribute('id').slice(1)); last = parseInt(last.getAttribute('id').slice(1)); while (r.head - first > r.wsz) { var obj = ebi('f' + (first++)); if (obj) obj.parentNode.removeChild(obj); } while (last - r.tail < r.wsz && last < r.tab.length - 2) { var obj = ebi('f' + (++last)); if (!obj) r.addrow(last); } }; r.drawcard = function (cat) { var cards = QSA('#u2cards>a>span'); if (cat == "q") { cards[4].innerHTML = r.ctr[cat]; return; } if (cat == "bz") { cards[3].innerHTML = r.ctr[cat]; return; } cards[2].innerHTML = r.ctr["ok"] + r.ctr["ng"]; if (cat == "ng") { cards[1].innerHTML = r.ctr[cat]; } if (cat == "ok") { cards[0].innerHTML = r.ctr[cat]; } }; r.changecard = function (card) { r.act = card; r.precard = has(["ok", "ng", "done"], r.act) ? {} : r.act == "bz" ? { "ok": 1, "ng": 1 } : { "ok": 1, "ng": 1, "bz": 1 }; r.postcard = has(["ok", "ng", "done"], r.act) ? { "bz": 1, "q": 1 } : r.act == "bz" ? { "q": 1 } : {}; r.head = -1; r.tail = -1; var html = []; for (var a = 0; a < r.tab.length; a++) { var rt = r.tab[a].in; if (r.is_act(rt)) { html.push(r.genrow(a, true)); r.tail = a; if (r.head == -1) r.head = a; } } if (r.head == -1) { for (var a = 0; a < r.tab.length; a++) { var rt = r.tab[a].in; if (r.precard[rt]) { r.head = a + 1; r.tail = a; } else if (r.postcard[rt]) { r.head = a; r.tail = a - 1; break; } } } if (r.head < 0) r.head = 0; if (card == "bz") { for (var a = r.head - 1; a >= r.head - r.wsz && a >= 0; a--) { html.unshift(r.genrow(a, true).replace(/>
excessive filereader latency (" + td + " ms), increasing readahead
"; min_filebuf = 32 * 1024 * 1024; } } hash_calc(nch, e.target.result); } reader.onload = function (e) { try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); } }; reader.onerror = function () { var err = reader.error + ''; var handled = false; if (err.indexOf('NotReadableError') !== -1 || // win10-chrome defender err.indexOf('NotFoundError') !== -1 // macos-firefox permissions ) { pvis.seth(t.n, 1, 'OS-error'); pvis.seth(t.n, 2, err); handled = true; } if (handled) { pvis.move(t.n, 'ng'); apop(st.busy.hash, t); st.bytes.finished += t.size; return tasker(); } toast.err(0, 'y o u b r o k e i t\nfile: ' + t.name + '\nerror: ' + err); }; reader.readAsArrayBuffer( bobslice.call(t.fobj, car, cdr)); return true; }; var hash_calc = function (nch, buf) { while (segm_next()); var hash_done = function (hashbuf) { var hslice = new Uint8Array(hashbuf).subarray(0, 33), b64str = buf2b64(hslice); hashtab[nch] = b64str; t.hash.push(nch); pvis.hashed(t); bpend -= buf.byteLength; if (t.hash.length < nchunks) { return segm_next(); } t.hash = []; for (var a = 0; a < nchunks; a++) { t.hash.push(hashtab[a]); } t.t_hashed = Date.now(); pvis.seth(t.n, 2, 'hashing done'); pvis.seth(t.n, 1, '📦 wait'); apop(st.busy.hash, t); st.todo.handshake.push(t); tasker(); }; if (subtle) subtle.digest('SHA-512', buf).then(hash_done); else setTimeout(function () { var u8buf = new Uint8Array(buf); if (sha_js == 'hw') { hashwasm.sha512(u8buf).then(function (v) { hash_done(hex2u8(v)) }); } else { var hasher = new asmCrypto.Sha512(); hasher.process(u8buf); hasher.finish(); hash_done(hasher.result); } }, 1); }; t.t_hashing = Date.now(); segm_next(); } ///// //// /// head // function exec_head() { var t = st.todo.head.shift(); st.busy.head.push(t); var xhr = new XMLHttpRequest(); xhr.onerror = function () { console.log('head onerror, retrying', t); apop(st.busy.head, t); st.todo.head.unshift(t); tasker(); }; function orz(e) { var ok = false; if (xhr.status == 200) { var srv_sz = xhr.getResponseHeader('Content-Length'), srv_ts = xhr.getResponseHeader('Last-Modified'); ok = t.size == srv_sz; if (ok && datechk) { srv_ts = new Date(srv_ts) / 1000; ok = Math.abs(srv_ts - t.lmod) < 2; } } apop(st.busy.head, t); if (!ok) return push_t(st.todo.hash, t); t.done = true; st.bytes.hashed += t.size; st.bytes.finished += t.size; pvis.seth(t.n, 1, 'YOLO'); pvis.seth(t.n, 2, "turbo'd"); pvis.move(t.n, 'ok'); }; xhr.onload = function (e) { try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); } }; xhr.open('HEAD', t.purl + uricom_enc(t.name), true); xhr.send(); } ///// //// /// handshake // function exec_handshake() { var t = st.todo.handshake.shift(), keepalive = t.keepalive, me = Date.now(); st.busy.handshake.push(t); t.keepalive = undefined; t.t_busied = me; if (keepalive) console.log("sending keepalive handshake", t); var xhr = new XMLHttpRequest(); xhr.onerror = function () { if (t.t_busied != me) { console.log('zombie handshake onerror,', t); return; } console.log('handshake onerror, retrying', t); apop(st.busy.handshake, t); st.todo.handshake.unshift(t); t.keepalive = keepalive; tasker(); }; function orz(e) { if (t.t_busied != me) { console.log('zombie handshake onload,', t); return; } if (xhr.status == 200) { t.t_handshake = Date.now(); if (keepalive) { apop(st.busy.handshake, t); return; } var response = JSON.parse(xhr.responseText); if (!response.name) { var msg = '', smsg = ''; if (!response || !response.hits || !response.hits.length) { smsg = '404'; msg = ('not found on server (explain)'); } else { smsg = 'found'; var hit = response.hits[0], msg = linksplit(hit.rp).join(''), tr = unix2iso(hit.ts), tu = unix2iso(t.lmod), diff = parseInt(t.lmod) - parseInt(hit.ts), cdiff = (Math.abs(diff) <= 2) ? '3c0' : 'f0b', sdiff = 'diff ' + diff; msg += '') === 0) rsp = rsp.slice(5); if (rsp.indexOf('rate-limit ') !== -1) { var penalty = rsp.replace(/.*rate-limit /, "").split(' ')[0]; console.log("rate-limit: " + penalty); t.cooldown = Date.now() + parseFloat(penalty) * 1000; apop(st.busy.handshake, t); st.todo.handshake.unshift(t); return; } st.bytes.finished += t.size; if (rsp.indexOf('partial upload exists') !== -1 || rsp.indexOf('file already exists') !== -1) { err = rsp; ofs = err.indexOf('\n/'); if (ofs !== -1) { err = err.slice(0, ofs + 1) + linksplit(err.slice(ofs + 2)).join(' '); } } if (err != "") { pvis.seth(t.n, 1, "ERROR"); pvis.seth(t.n, 2, err); pvis.move(t.n, 'ng'); apop(st.busy.handshake, t); tasker(); return; } toast.err(0, "server broke; hs-err {0} on file [{1}]:\n".format( xhr.status, t.name) + ( (xhr.response && xhr.response.err) || (xhr.responseText && xhr.responseText) || "no further information")); } } xhr.onload = function (e) { try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); } }; var req = { "name": t.name, "size": t.size, "lmod": t.lmod, "hash": t.hash }; if (fsearch) req.srch = 1; xhr.open('POST', t.purl, true); xhr.responseType = 'text'; xhr.send(JSON.stringify(req)); } ///// //// /// upload // function exec_upload() { var upt = st.todo.upload.shift(); st.busy.upload.push(upt); var npart = upt.npart, t = st.files[upt.nfile]; if (!t.t_uploading) t.t_uploading = Date.now(); pvis.seth(t.n, 1, "🚀 send"); var chunksize = get_chunksize(t.size), car = npart * chunksize, cdr = car + chunksize; if (cdr >= t.size) cdr = t.size; function orz(xhr) { var txt = ((xhr.response && xhr.response.err) || xhr.responseText) + ''; if (xhr.status == 200) { pvis.prog(t, npart, cdr - car); st.bytes.finished += cdr - car; st.bytes.uploaded += cdr - car; t.bytes_uploaded += cdr - car; } else if (txt.indexOf('already got that') !== -1) { console.log("ignoring dupe-segment error", t); } else { toast.err(0, "server broke; cu-err {0} on file [{1}]:\n".format( xhr.status, t.name) + (txt || "no further information")); return; } apop(st.busy.upload, upt); apop(t.postlist, npart); if (!t.postlist.length) { t.t_uploaded = Date.now(); pvis.seth(t.n, 1, 'verifying'); st.todo.handshake.unshift(t); } tasker(); } function do_send() { var xhr = new XMLHttpRequest(); xhr.upload.onprogress = function (xev) { pvis.prog(t, npart, xev.loaded); }; xhr.onload = function (xev) { try { orz(xhr); } catch (ex) { vis_exh(ex + '', '', '', '', ex); } }; xhr.onerror = function (xev) { if (crashed) return; console.log('chunkpit onerror, retrying', t); do_send(); }; xhr.open('POST', t.purl, true); xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]); xhr.setRequestHeader("X-Up2k-Wark", t.wark); xhr.setRequestHeader('Content-Type', 'application/octet-stream'); if (xhr.overrideMimeType) xhr.overrideMimeType('Content-Type', 'application/octet-stream'); xhr.responseType = 'text'; xhr.send(bobslice.call(t.fobj, car, cdr)); } do_send(); } ///// //// /// config ui // function onresize(e) { var bar = ebi('ops'), wpx = window.innerWidth, fpx = parseInt(getComputedStyle(bar)['font-size']), wem = wpx * 1.0 / fpx, wide = wem > 54, parent = ebi(wide && has(perms, 'write') ? 'u2btn_cw' : 'u2btn_ct'), btn = ebi('u2btn'); //console.log([wpx, fpx, wem]); if (btn.parentNode !== parent) { parent.appendChild(btn); ebi('u2conf').setAttribute('class', wide ? 'has_btn' : ''); ebi('u2cards').setAttribute('class', wide ? 'w' : ''); ebi('u2etaw').setAttribute('class', wide ? 'w' : ''); } } window.addEventListener('resize', onresize); onresize(); if (is_touch) { // android-chrome wobbles for a bit; firefox / iOS-safari are OK setTimeout(onresize, 20); setTimeout(onresize, 100); setTimeout(onresize, 500); } var o = QSA('#u2conf *[tt]'); for (var a = o.length - 1; a >= 0; a--) { o[a].parentNode.getElementsByTagName('input')[0].setAttribute('tt', o[a].getAttribute('tt')); } tt.att(QS('#u2conf')); function bumpthread2(e) { if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing) return; if (e.code == 'ArrowUp') bumpthread(1); if (e.code == 'ArrowDown') bumpthread(-1); } function bumpthread(dir) { try { dir.stopPropagation(); dir.preventDefault(); } catch (ex) { } var obj = ebi('nthread'); if (dir.target) { clmod(obj, 'err', 1); var v = Math.floor(parseInt(obj.value)); if (v < 0 || v > 64 || v !== v) return; parallel_uploads = v; swrite('nthread', v); clmod(obj, 'err'); return; } parallel_uploads += dir; if (parallel_uploads < 0) parallel_uploads = 0; if (parallel_uploads > 16) parallel_uploads = 16; obj.value = parallel_uploads; bumpthread({ "target": 1 }) } function tgl_multitask() { multitask = !multitask; bcfg_set('multitask', multitask); } function tgl_ask_up() { ask_up = !ask_up; bcfg_set('ask_up', ask_up); } function tgl_fsearch() { set_fsearch(!fsearch); } function tgl_turbo() { turbo = !turbo; bcfg_set('u2turbo', turbo); draw_turbo(); } function tgl_datechk() { datechk = !datechk; bcfg_set('u2tdate', datechk); } function draw_turbo() { var msgu = 'WARNING: turbo enabled, client may not detect and resume incomplete uploads; see turbo-button tooltip
', msgs = 'WARNING: turbo enabled, search may give false-positives; see turbo-button tooltip
', msg = fsearch ? msgs : msgu, omsg = fsearch ? msgu : msgs, html = ebi('u2foot').innerHTML, ohtml = html; if (turbo && html.indexOf(msg) === -1) html = html.replace(omsg, '') + msg; else if (!turbo) html = html.replace(msgu, '').replace(msgs, ''); if (html !== ohtml) ebi('u2foot').innerHTML = html; } draw_turbo(); function set_fsearch(new_state) { var fixed = false; if (!ebi('fsearch')) { new_state = false; } else if (perms.length) { if (!has(perms, 'write')) { new_state = true; fixed = true; } if (!has(perms, 'read')) { new_state = false; fixed = true; } } if (new_state !== undefined) { fsearch = new_state; bcfg_set('fsearch', fsearch); } try { QS('label[for="fsearch"]').style.display = QS('#fsearch').style.display = fixed ? 'none' : ''; } catch (ex) { } try { var ico = fsearch ? '🔎' : '🚀', desc = fsearch ? 'Search' : 'Upload'; clmod(ebi('op_up2k'), 'srch', fsearch); ebi('u2bm').innerHTML = ico + ' ' + desc + ''; } catch (ex) { } draw_turbo(); onresize(); } function tgl_flag_en() { flag_en = !flag_en; bcfg_set('flag_en', flag_en); apply_flag_cfg(); } function apply_flag_cfg() { if (flag_en && !flag) { try { flag = up2k_flagbus(); } catch (ex) { console.log("flag error: " + ex.toString()); tgl_flag_en(); } } else if (!flag_en && flag) { flag.ch.close(); flag = false; } } function nop(e) { ev(e); this.click(); } ebi('nthread_add').onclick = function (e) { ev(e); bumpthread(1); }; ebi('nthread_sub').onclick = function (e) { ev(e); bumpthread(-1); }; ebi('nthread').onkeydown = bumpthread2; ebi('nthread').addEventListener('input', bumpthread, false); ebi('multitask').addEventListener('click', tgl_multitask, false); ebi('ask_up').addEventListener('click', tgl_ask_up, false); ebi('flag_en').addEventListener('click', tgl_flag_en, false); ebi('u2turbo').addEventListener('click', tgl_turbo, false); ebi('u2tdate').addEventListener('click', tgl_datechk, false); var o = ebi('fsearch'); if (o) o.addEventListener('click', tgl_fsearch, false); var nodes = ebi('u2conf').getElementsByTagName('a'); for (var a = nodes.length - 1; a >= 0; a--) nodes[a].addEventListener('touchend', nop, false); ebi('u2etas').onclick = function () { clmod(ebi('u2etas'), 'o', 't'); }; set_fsearch(); bumpthread({ "target": 1 }); if (parallel_uploads < 1) bumpthread(1); return { "init_deps": init_deps, "set_fsearch": set_fsearch } } function warn_uploader_busy(e) { e.preventDefault(); e.returnValue = ''; return "upload in progress, click abort and use the file-tree to navigate instead"; } tt.init(); if (QS('#op_up2k.act')) goto_up2k(); apply_perms(perms); (function () { goto(); var op = sread('opmode'); if (op !== null && op !== '.') try { goto(op); } catch (ex) { } })();