From 17c465bed7cce621d31f291554720b97adb7aeba Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 15 Sep 2022 23:43:40 +0200 Subject: [PATCH] lazyload big folders; closes #11 --- copyparty/web/browser.css | 17 +++- copyparty/web/browser.js | 164 +++++++++++++++++++++++++++++++++++--- copyparty/web/md.js | 4 +- copyparty/web/util.js | 15 ++++ 4 files changed, 185 insertions(+), 15 deletions(-) diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index ee2de85f..c1344962 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -1583,11 +1583,26 @@ html.y #tree.nowrap .ntree a+a:hover { } #files>thead>tr>th.min, #files td.min { - display: none; + display: none; } #files td:nth-child(2n) { color: var(--tab-alt); } +#plazy { + width: 1px; + height: 1px; + overflow: hidden; +} +#blazy { + text-align: center; + font-size: 1.2em; + margin: 1em 0; +} +#blazy code, +#blazy a { + font-size: 1.1em; + padding: 0 .2em; +} .opwide, #op_unpost, #srch_form { diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index ee8d635c..6ceede26 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -101,6 +101,7 @@ var Ls = { "cl_ziptype": "folder download", "cl_uopts": "up2k switches", "cl_favico": "favicon", + "cl_bigdir": "big dirs", "cl_keytype": "key notation", "cl_hiddenc": "hidden columns", "cl_reset": "(reset)", @@ -124,6 +125,9 @@ var Ls = { "cft_fg": "foreground color", "cft_bg": "background color", + "cdt_lim": "max number of files to show in a folder", + "cdt_ask": "when scrolling to the bottom,$Ninstead of loading more files,$Nask what to do", + "tt_entree": "show navpane (directory tree sidebar)$NHotkey: B", "tt_detree": "show breadcrumbs$NHotkey: B", "tt_visdir": "scroll to selected folder", @@ -168,6 +172,8 @@ var Ls = { "f_chide": 'this will hide the column «{0}»\n\nyou can unhide columns in the settings tab', "f_bigtxt": "this file is {0} MiB large -- really view as text?", + "fbd_more": '
showing {0} of {1} files; show {2} or show all
', + "fbd_all": '
showing {0} of {1} files; show all
', "ft_paste": "paste {0} items$NHotkey: ctrl-V", "fr_eperm": 'cannot rename:\nyou do not have “move” permission in this folder', @@ -448,6 +454,7 @@ var Ls = { "cl_ziptype": "nedlastning av mapper", "cl_uopts": "up2k-brytere", "cl_favico": "favicon", + "cl_bigdir": "store mapper", "cl_keytype": "notasjon for musikalsk dur", "cl_hiddenc": "skjulte kolonner", "cl_reset": "(nullstill)", @@ -471,6 +478,9 @@ var Ls = { "cft_fg": "farge", "cft_bg": "bakgrunnsfarge", + "cdt_lim": "maks antall filer å vise per mappe", + "cdt_ask": "vis knapper for å laste flere filer nederst på siden istedenfor å gradvis laste mer av mappen når man scroller ned", + "tt_entree": "bytt til mappehierarki$NSnarvei: B", "tt_detree": "bytt til tradisjonell sti-visning$NSnarvei: B", "tt_visdir": "bla ned til den åpne mappen", @@ -515,6 +525,8 @@ var Ls = { "f_chide": 'dette vil skjule kolonnen «{0}»\n\nfanen for "andre innstillinger" lar deg vise kolonnen igjen', "f_bigtxt": "denne filen er hele {0} MiB -- vis som tekst?", + "fbd_more": '
viser {0} av {1} filer; vis {2} eller vis alle
', + "fbd_all": '
viser {0} av {1} filer; vis alle
', "ft_paste": "Lim inn {0} filer$NSnarvei: ctrl-V", "fr_eperm": 'kan ikke endre navn:\ndu har ikke “move”-rettigheten i denne mappen', @@ -827,6 +839,9 @@ ebi('op_up2k').innerHTML = ( ); +ebi('wrap').insertBefore(mknod('div', 'lazy'), ebi('epi')); + + (function () { var o = mknod('div'); o.innerHTML = ( @@ -887,6 +902,14 @@ ebi('op_cfg').innerHTML = ( ' \n' + ' \n' + '\n' + + '
\n' + + '

' + L.cl_bigdir + '

\n' + + '
\n' + + ' ' + + ' ask\n' + + ' \n' + + '
\n' + + '
\n' + '

' + L.cl_keytype + '

\n' + '

' + L.cl_hiddenc + ' ' + L.cl_reset + '

' ); @@ -1015,7 +1038,7 @@ function set_files_html(html) { par.removeChild(files); files = mknod('div'); files.innerHTML = '' + html + '
'; - par.insertBefore(files.childNodes[0], ebi('epi')); + par.insertBefore(files.childNodes[0], ebi('lazy')); files = ebi('files'); return files; } @@ -3375,7 +3398,7 @@ var showfile = (function () { lang = r.getlang(name), is_md = lang == 'md'; - ebi('files').style.display = ebi('gfiles').style.display = ebi('pro').style.display = ebi('epi').style.display = 'none'; + ebi('files').style.display = ebi('gfiles').style.display = ebi('lazy').style.display = ebi('pro').style.display = ebi('epi').style.display = 'none'; ebi('dldoc').setAttribute('href', url); var wr = ebi('bdoc'), @@ -3629,7 +3652,7 @@ var thegrid = (function () { var vis = has(perms, "read"); gfiles.style.display = vis && r.en ? '' : 'none'; lfiles.style.display = vis && !r.en ? '' : 'none'; - ebi('pro').style.display = ebi('epi').style.display = ebi('treeul').style.display = ebi('treepar').style.display = ''; + ebi('pro').style.display = ebi('epi').style.display = ebi('lazy').style.display = ebi('treeul').style.display = ebi('treepar').style.display = ''; ebi('bdoc').style.display = 'none'; clmod(ebi('wrap'), 'doc'); qsr('#docname'); @@ -4496,6 +4519,9 @@ document.onkeydown = function (e) { })(); function aligngriditems() { + if (!window.treectl) + return; + var em2px = parseFloat(getComputedStyle(ebi('ggrid')).fontSize); var gridsz = getComputedStyle(document.getElementsByTagName('html')[0]).getPropertyValue('--grid-sz').slice(0, -2); var gridwidth = ebi('ggrid').clientWidth; @@ -4504,10 +4530,8 @@ function aligngriditems() { if (((griditemcount * em2px) * gridsz) + totalgapwidth < gridwidth) { ebi('ggrid').style.justifyContent = 'left'; - } else if (localStorage.getItem('entreed') == 'na') { - ebi('ggrid').style.justifyContent = 'center'; - } else if (localStorage.getItem('entreed') == 'tree') { - ebi('ggrid').style.justifyContent = 'space-between'; + } else { + ebi('ggrid').style.justifyContent = treectl.hidden ? 'center' : 'space-between'; } } window.addEventListener('resize', aligngriditems); @@ -4537,6 +4561,13 @@ var treectl = (function () { setwrap(bcfg_bind(r, 'wtree', 'wraptree', true, setwrap)); setwrap(bcfg_bind(r, 'parpane', 'parpane', true, onscroll)); bcfg_bind(r, 'htree', 'hovertree', false, reload_tree); + bcfg_bind(r, 'ask', 'bd_ask', false); + ebi('bd_lim').value = r.lim = icfg_get('bd_lim'); + ebi('bd_lim').oninput = function (e) { + var n = parseInt(this.value); + swrite('bd_lim', r.lim = (isNum(n) ? n : 0) || 1000); + }; + r.nvis = r.lim; function setwrap(v) { clmod(ebi('tree'), 'nowrap', !v); @@ -4662,7 +4693,7 @@ var treectl = (function () { } y = ebi('treeh').offsetHeight; if (!fixedpos) - y += tree.offsetTop - document.documentElement.scrollTop; + y += tree.offsetTop - yscroll(); y = (y - 3) + 'px'; if (parp.style.top != y) @@ -4916,8 +4947,10 @@ var treectl = (function () { if (hpush && !no_tree) get_tree('.', xhr.top); + r.nvis = r.lim; r.nextdir = xhr.top; enspin(thegrid.en ? '#gfiles' : '#files'); + window.removeEventListener('scroll', r.tscroll); } function treegrow(e) { @@ -4955,6 +4988,10 @@ var treectl = (function () { return; } + for (var a = 0; a < res.files.length; a++) + if (res.files[a].tags === undefined) + res.files[a].tags = {}; + srvinf = res.srvinf; try { ebi('srv_info').innerHTML = ebi('srv_info2').innerHTML = '' + res.srvinf + ''; @@ -4990,14 +5027,32 @@ var treectl = (function () { } r.gentab = function (top, res) { - r.lsc = res; var nodes = res.dirs.concat(res.files), html = mk_files_header(res.taglist), + sel = r.lsc === res ? msel.getsel() : [], + plain = [], seen = {}; + r.lsc = res; + nodes = sortfiles(nodes); + window.removeEventListener('scroll', r.tscroll); + r.trunc = nodes.length > r.nvis && location.hash.length < 2; + if (r.trunc) { + for (var a = r.lim; a < nodes.length; a++) { + var tn = nodes[a], + tns = Object.keys(tn.tags); + + plain.push(uricom_dec(tn.href.split('?')[0])); + + for (var b = 0; b < tns.length; b++) + if (has(res.taglist, tns[b])) + plain.push(tn.tags[tns[b]]); + } + nodes = nodes.slice(0, r.nvis); + } + showfile.files = []; html.push(''); - nodes = sortfiles(nodes); for (var a = 0; a < nodes.length; a++) { var tn = nodes[a], bhref = tn.href.split('?')[0], @@ -5037,6 +5092,13 @@ var treectl = (function () { html.push(''); html = html.join('\n'); set_files_html(html); + if (r.trunc) { + r.setlazy(plain); + if (!r.ask) { + window.addEventListener('scroll', r.tscroll); + setTimeout(r.tscroll, 100); + } + } function asdf() { showfile.mktree(); @@ -5050,6 +5112,9 @@ var treectl = (function () { apply_perms(res.perms); fileman.render(); } + if (sel.length) + msel.loadsel(sel); + setTimeout(eval_hash, 1); } @@ -5090,6 +5155,65 @@ var treectl = (function () { setTimeout(eval_hash, 1); }; + r.setlazy = function (plain) { + var html = ['
', esc(plain.join(' ')), '
'], + all = r.lsc.files.length + r.lsc.dirs.length, + nxt = r.nvis * 4; + + if (r.ask) + html.push((nxt >= all ? L.fbd_all : L.fbd_more).format(r.nvis, all, nxt)); + + ebi('lazy').innerHTML = html.join('\n'); + + try { + ebi('bd_all').onclick = function (e) { + ev(e); + r.showmore(all); + }; + ebi('bd_more').onclick = function (e) { + ev(e); + r.showmore(nxt); + }; + } + catch (ex) { } + }; + + r.showmore = function (n) { + window.removeEventListener('scroll', r.tscroll); + console.log('nvis {0} -> {1}'.format(r.nvis, n)); + r.nvis = n; + ebi('lazy').innerHTML = ''; + ebi('wrap').style.opacity = 0.4; + setTimeout(function () { + r.gentab(get_evpath(), r.lsc); + ebi('wrap').style.opacity = 'unset'; + }, 1); + }; + + r.tscroll = function () { + var el = r.trunc ? ebi('plazy') : null; + if (!el || ebi('lazy').style.display || ebi('unsearch')) + return; + + var sy = yscroll() + window.innerHeight, + ty = el.offsetTop; + + if (sy <= ty) + return; + + window.removeEventListener('scroll', r.tscroll); + + var all = r.lsc.files.length + r.lsc.dirs.length; + if (r.nvis * 16 <= all) { + console.log("{0} ({1} * 16) <= {2}".format(r.nvis * 16, r.nvis, all)); + r.showmore(r.nvis * 4); + } + else { + console.log("{0} ({1} * 16) > {2}".format(r.nvis * 16, r.nvis, all)); + r.showmore(all); + } + }; + function parsetree(res, top) { var ret = ''; for (var a = 0; a < res.a.length; a++) { @@ -5768,6 +5892,21 @@ var msel = (function () { } }; + r.loadsel = function (sel) { + r.sel = []; + r.load(); + + var vsel = new Set(); + for (var a = 0; a < sel.length; a++) + vsel.add(sel[a].vp); + + for (var a = 0; a < r.all.length; a++) + if (vsel.has(r.all[a].vp)) + clmod(ebi(r.all[a].id).closest('tr'), 'sel', 1); + + r.selui(); + }; + r.getsel = function () { r.load(); return r.sel; @@ -6318,8 +6457,9 @@ function reload_browser() { mp.read_order(); }); - for (var a = 0; a < 2; a++) - clmod(ebi(a ? 'pro' : 'epi'), 'hidden', ebi('unsearch')); + var ns = ['pro', 'epi', 'lazy'] + for (var a = 0; a < ns.length; a++) + clmod(ebi(ns[a]), 'hidden', ebi('unsearch')); if (window['up2k']) up2k.set_fsearch(); diff --git a/copyparty/web/md.js b/copyparty/web/md.js index 340546b3..4d8522f7 100644 --- a/copyparty/web/md.js +++ b/copyparty/web/md.js @@ -395,7 +395,7 @@ function init_toc() { // collect vertical position of all toc items (headers in document) function freshen_offsets() { - var top = window.pageYOffset || document.documentElement.scrollTop; + var top = yscroll(); for (var a = anchors.length - 1; a >= 0; a--) { var y = top + anchors[a].elm.getBoundingClientRect().top; y = Math.round(y * 10.0) / 10; @@ -411,7 +411,7 @@ function init_toc() { if (anchors.length == 0) return; - var ptop = window.pageYOffset || document.documentElement.scrollTop; + var ptop = yscroll(); var hit = anchors.length - 1; for (var a = 0; a < anchors.length; a++) { if (anchors[a].y >= ptop - 8) { //??? diff --git a/copyparty/web/util.js b/copyparty/web/util.js index 2f416a52..55ca2f47 100644 --- a/copyparty/web/util.js +++ b/copyparty/web/util.js @@ -390,6 +390,21 @@ if (window.matchMedia) { } +function yscroll() { + if (document.documentElement.scrollTop) { + return (window.yscroll = function () { + return document.documentElement.scrollTop; + })(); + } + if (window.pageYOffset) { + return (window.yscroll = function () { + return window.pageYOffset; + })(); + } + return 0; +} + + function showsort(tab) { var v, vn, v1, v2, th = tab.tHead, sopts = jread('fsort', [["href", 1, ""]]);