mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 03:53:31 +00:00 
			
		
		
		
	Compare commits
	
		
			9 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | eafe2098b6 | ||
|  | 2bc6a20d71 | ||
|  | 8b502a7235 | ||
|  | 37567844af | ||
|  | 2f6c4e0e34 | ||
|  | 1c7cc4cb2b | ||
|  | f83db3648e | ||
|  | b164aa00d4 | ||
|  | a2d866d0c2 | 
| @@ -123,20 +123,17 @@ def main(): | ||||
|             """ | ||||
|         ), | ||||
|     ) | ||||
|     ap.add_argument( | ||||
|         "-c", metavar="PATH", type=str, action="append", help="add config file" | ||||
|     ) | ||||
|     ap.add_argument("-c", metavar="PATH", type=str, action="append", help="add config file") | ||||
|     ap.add_argument("-i", metavar="IP", type=str, default="0.0.0.0", help="ip to bind") | ||||
|     ap.add_argument("-p", metavar="PORT", type=int, default=3923, help="port to bind") | ||||
|     ap.add_argument("-nc", metavar="NUM", type=int, default=64, help="max num clients") | ||||
|     ap.add_argument( | ||||
|         "-j", metavar="CORES", type=int, default=1, help="max num cpu cores" | ||||
|     ) | ||||
|     ap.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores") | ||||
|     ap.add_argument("-a", metavar="ACCT", type=str, action="append", help="add account") | ||||
|     ap.add_argument("-v", metavar="VOL", type=str, action="append", help="add volume") | ||||
|     ap.add_argument("-q", action="store_true", help="quiet") | ||||
|     ap.add_argument("-ed", action="store_true", help="enable ?dots") | ||||
|     ap.add_argument("-emp", action="store_true", help="enable markdown plugins") | ||||
|     ap.add_argument("-mcr", metavar="SEC", type=int, default=60, help="md-editor mod-chk rate") | ||||
|     ap.add_argument("-nw", action="store_true", help="disable writes (benchmark)") | ||||
|     ap.add_argument("-nih", action="store_true", help="no info hostname") | ||||
|     ap.add_argument("-nid", action="store_true", help="no info disk-usage") | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| # coding: utf-8 | ||||
|  | ||||
| VERSION = (0, 5, 5) | ||||
| VERSION = (0, 5, 7) | ||||
| CODENAME = "fuse jelly" | ||||
| BUILD_DT = (2020, 11, 27) | ||||
| BUILD_DT = (2020, 11, 30) | ||||
|  | ||||
| S_VERSION = ".".join(map(str, VERSION)) | ||||
| S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) | ||||
|   | ||||
| @@ -964,6 +964,7 @@ class HttpCli(object): | ||||
|             "title": html_escape(self.vpath), | ||||
|             "lastmod": int(ts_md * 1000), | ||||
|             "md_plug": "true" if self.args.emp else "false", | ||||
|             "md_chk_rate": self.args.mcr, | ||||
|             "md": "", | ||||
|         } | ||||
|         sz_html = len(template.render(**targs).encode("utf-8")) | ||||
|   | ||||
| @@ -68,6 +68,8 @@ | ||||
|         </div> | ||||
|     </div> | ||||
|      | ||||
|     <script src="/.cpr/util.js{{ ts }}"></script> | ||||
|  | ||||
|     {%- if can_read %} | ||||
|     <script src="/.cpr/browser.js{{ ts }}"></script> | ||||
|     {%- endif %} | ||||
|   | ||||
| @@ -1,75 +1,9 @@ | ||||
| "use strict"; | ||||
|  | ||||
| // error handler for mobile devices | ||||
| function hcroak(msg) { | ||||
| 	document.body.innerHTML = msg; | ||||
| 	window.onerror = undefined; | ||||
| 	throw 'fatal_err'; | ||||
| } | ||||
| function croak(msg) { | ||||
| 	document.body.textContent = msg; | ||||
| 	window.onerror = undefined; | ||||
| 	throw msg; | ||||
| } | ||||
| function esc(txt) { | ||||
| 	return txt.replace(/[&"<>]/g, function (c) { | ||||
| 		return { | ||||
| 			'&': '&', | ||||
| 			'"': '"', | ||||
| 			'<': '<', | ||||
| 			'>': '>' | ||||
| 		}[c]; | ||||
| 	}); | ||||
| } | ||||
| window.onerror = function (msg, url, lineNo, columnNo, error) { | ||||
| 	window.onerror = undefined; | ||||
| 	var html = ['<h1>you hit a bug!</h1><p>please screenshot this error and send me a copy arigathanks gozaimuch (ed/irc.rizon.net or ed#2644)</p><p>', | ||||
| 		esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>']; | ||||
|  | ||||
| 	if (error) { | ||||
| 		var find = ['desc', 'stack', 'trace']; | ||||
| 		for (var a = 0; a < find.length; a++) | ||||
| 			if (String(error[find[a]]) !== 'undefined') | ||||
| 				html.push('<h2>' + find[a] + '</h2>' + | ||||
| 					esc(String(error[find[a]])).replace(/\n/g, '<br />\n')); | ||||
| 	} | ||||
| 	document.body.style.fontSize = '0.8em'; | ||||
| 	document.body.style.padding = '0 1em 1em 1em'; | ||||
| 	hcroak(html.join('\n')); | ||||
| }; | ||||
|  | ||||
|  | ||||
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith | ||||
| if (!String.prototype.endsWith) { | ||||
| 	String.prototype.endsWith = function (search, this_len) { | ||||
| 		if (this_len === undefined || this_len > this.length) { | ||||
| 			this_len = this.length; | ||||
| 		} | ||||
| 		return this.substring(this_len - search.length, this_len) === search; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|  | ||||
| // https://stackoverflow.com/a/950146 | ||||
| function import_js(url, cb) { | ||||
| 	var head = document.head || document.getElementsByTagName('head')[0]; | ||||
| 	var script = document.createElement('script'); | ||||
| 	script.type = 'text/javascript'; | ||||
| 	script.src = url; | ||||
|  | ||||
| 	script.onreadystatechange = cb; | ||||
| 	script.onload = cb; | ||||
|  | ||||
| 	head.appendChild(script); | ||||
| } | ||||
|  | ||||
|  | ||||
| function o(id) { | ||||
| 	return document.getElementById(id); | ||||
| } | ||||
| window.onerror = vis_exh; | ||||
|  | ||||
| function dbg(msg) { | ||||
| 	o('path').innerHTML = msg; | ||||
| 	ebi('path').innerHTML = msg; | ||||
| } | ||||
|  | ||||
| function ev(e) { | ||||
| @@ -78,40 +12,7 @@ function ev(e) { | ||||
| 	return e; | ||||
| } | ||||
|  | ||||
|  | ||||
| function sortTable(table, col) { | ||||
| 	var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows | ||||
| 		th = table.tHead.rows[0].cells, | ||||
| 		tr = Array.prototype.slice.call(tb.rows, 0), | ||||
| 		i, reverse = th[col].className == 'sort1' ? -1 : 1; | ||||
| 	for (var a = 0, thl = th.length; a < thl; a++) | ||||
| 		th[a].className = ''; | ||||
| 	th[col].className = 'sort' + reverse; | ||||
| 	var stype = th[col].getAttribute('sort'); | ||||
| 	tr = tr.sort(function (a, b) { | ||||
| 		var v1 = a.cells[col].textContent.trim(); | ||||
| 		var v2 = b.cells[col].textContent.trim(); | ||||
| 		if (stype == 'int') { | ||||
| 			v1 = parseInt(v1.replace(/,/g, '')); | ||||
| 			v2 = parseInt(v2.replace(/,/g, '')); | ||||
| 			return reverse * (v1 - v2); | ||||
| 		} | ||||
| 		return reverse * (v1.localeCompare(v2)); | ||||
| 	}); | ||||
| 	for (i = 0; i < tr.length; ++i) tb.appendChild(tr[i]); | ||||
| } | ||||
| function makeSortable(table) { | ||||
| 	var th = table.tHead, i; | ||||
| 	th && (th = th.rows[0]) && (th = th.cells); | ||||
| 	if (th) i = th.length; | ||||
| 	else return; // if no `<thead>` then do nothing | ||||
| 	while (--i >= 0) (function (i) { | ||||
| 		th[i].onclick = function () { | ||||
| 			sortTable(table, i); | ||||
| 		}; | ||||
| 	}(i)); | ||||
| } | ||||
| makeSortable(o('files')); | ||||
| makeSortable(ebi('files')); | ||||
|  | ||||
|  | ||||
| // extract songs + add play column | ||||
| @@ -126,7 +27,7 @@ var mp = (function () { | ||||
| 	}; | ||||
| 	var re_audio = new RegExp('\.(opus|ogg|m4a|aac|mp3|wav|flac)$', 'i'); | ||||
|  | ||||
| 	var trs = document.getElementById('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); | ||||
| 	var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); | ||||
| 	for (var a = 0, aa = trs.length; a < aa; a++) { | ||||
| 		var tds = trs[a].getElementsByTagName('td'); | ||||
| 		var link = tds[1].getElementsByTagName('a')[0]; | ||||
| @@ -142,7 +43,7 @@ var mp = (function () { | ||||
| 	} | ||||
|  | ||||
| 	for (var a = 0, aa = tracks.length; a < aa; a++) | ||||
| 		o('trk' + a).onclick = ev_play; | ||||
| 		ebi('trk' + a).onclick = ev_play; | ||||
|  | ||||
| 	ret.vol = localStorage.getItem('vol'); | ||||
| 	if (ret.vol !== null) | ||||
| @@ -169,8 +70,8 @@ var mp = (function () { | ||||
| // toggle player widget | ||||
| var widget = (function () { | ||||
| 	var ret = {}; | ||||
| 	var widget = document.getElementById('widget'); | ||||
| 	var wtoggle = document.getElementById('wtoggle'); | ||||
| 	var widget = ebi('widget'); | ||||
| 	var wtoggle = ebi('wtoggle'); | ||||
| 	var touchmode = false; | ||||
| 	var side_open = false; | ||||
| 	var was_paused = true; | ||||
| @@ -199,7 +100,7 @@ var widget = (function () { | ||||
| 	ret.paused = function (paused) { | ||||
| 		if (was_paused != paused) { | ||||
| 			was_paused = paused; | ||||
| 			o('bplay').innerHTML = paused ? '▶' : '⏸'; | ||||
| 			ebi('bplay').innerHTML = paused ? '▶' : '⏸'; | ||||
| 		} | ||||
| 	}; | ||||
| 	var click_handler = function (e) { | ||||
| @@ -223,8 +124,8 @@ var widget = (function () { | ||||
| // buffer/position bar | ||||
| var pbar = (function () { | ||||
| 	var r = {}; | ||||
| 	r.bcan = o('barbuf'); | ||||
| 	r.pcan = o('barpos'); | ||||
| 	r.bcan = ebi('barbuf'); | ||||
| 	r.pcan = ebi('barpos'); | ||||
| 	r.bctx = r.bcan.getContext('2d'); | ||||
| 	r.pctx = r.pcan.getContext('2d'); | ||||
|  | ||||
| @@ -289,7 +190,7 @@ var pbar = (function () { | ||||
| // volume bar | ||||
| var vbar = (function () { | ||||
| 	var r = {}; | ||||
| 	r.can = o('pvol'); | ||||
| 	r.can = ebi('pvol'); | ||||
| 	r.ctx = r.can.getContext('2d'); | ||||
|  | ||||
| 	var bctx = r.ctx; | ||||
| @@ -386,7 +287,7 @@ var vbar = (function () { | ||||
| 		else | ||||
| 			play(0); | ||||
| 	}; | ||||
| 	o('bplay').onclick = function (e) { | ||||
| 	ebi('bplay').onclick = function (e) { | ||||
| 		ev(e); | ||||
| 		if (mp.au) { | ||||
| 			if (mp.au.paused) | ||||
| @@ -397,15 +298,15 @@ var vbar = (function () { | ||||
| 		else | ||||
| 			play(0); | ||||
| 	}; | ||||
| 	o('bprev').onclick = function (e) { | ||||
| 	ebi('bprev').onclick = function (e) { | ||||
| 		ev(e); | ||||
| 		bskip(-1); | ||||
| 	}; | ||||
| 	o('bnext').onclick = function (e) { | ||||
| 	ebi('bnext').onclick = function (e) { | ||||
| 		ev(e); | ||||
| 		bskip(1); | ||||
| 	}; | ||||
| 	o('barpos').onclick = function (e) { | ||||
| 	ebi('barpos').onclick = function (e) { | ||||
| 		if (!mp.au) { | ||||
| 			//dbg((new Date()).getTime()); | ||||
| 			return play(0); | ||||
| @@ -471,7 +372,7 @@ function ev_play(e) { | ||||
|  | ||||
|  | ||||
| function setclass(id, clas) { | ||||
| 	o(id).setAttribute('class', clas); | ||||
| 	ebi(id).setAttribute('class', clas); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -608,7 +509,7 @@ function show_modal(html) { | ||||
|  | ||||
| // hide fullscreen message | ||||
| function unblocked() { | ||||
| 	var dom = o('blocked'); | ||||
| 	var dom = ebi('blocked'); | ||||
| 	if (dom) | ||||
| 		dom.parentNode.removeChild(dom); | ||||
| } | ||||
| @@ -620,8 +521,8 @@ function autoplay_blocked(tid) { | ||||
| 		'<div id="blk_play"><a href="#" id="blk_go"></a></div>' + | ||||
| 		'<div id="blk_abrt"><a href="#" id="blk_na">Cancel<br />(show file list)</a></div>'); | ||||
|  | ||||
| 	var go = o('blk_go'); | ||||
| 	var na = o('blk_na'); | ||||
| 	var go = ebi('blk_go'); | ||||
| 	var na = ebi('blk_na'); | ||||
|  | ||||
| 	var fn = mp.tracks[mp.au.tid].split(/\//).pop(); | ||||
| 	fn = decodeURIComponent(fn.replace(/\+/g, ' ')); | ||||
|   | ||||
| @@ -126,7 +126,8 @@ write markdown (most html is 🙆 too) | ||||
| var last_modified = {{ lastmod }}; | ||||
| var md_opt = { | ||||
| 	link_md_as_html: false, | ||||
| 	allow_plugins: {{ md_plug }} | ||||
| 	allow_plugins: {{ md_plug }}, | ||||
| 	modpoll_freq: {{ md_chk_rate }} | ||||
| }; | ||||
|  | ||||
| (function () { | ||||
| @@ -144,17 +145,11 @@ var md_opt = { | ||||
| 		toggle(); | ||||
| })(); | ||||
|  | ||||
| if (!String.startsWith) { | ||||
| 	String.prototype.startsWith = function(s, i) { | ||||
| 		i = i>0 ? i|0 : 0; | ||||
| 		return this.substring(i, i + s.length) === s; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| 	</script> | ||||
|     <script src="/.cpr/util.js"></script> | ||||
| 	<script src="/.cpr/deps/marked.full.js"></script> | ||||
| 	<script src="/.cpr/md.js"></script> | ||||
| 	{%- if edit %} | ||||
| 		<script src="/.cpr/md2.js"></script> | ||||
| 	<script src="/.cpr/md2.js"></script> | ||||
| 	{%- endif %} | ||||
| </body></html> | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| var dom_toc = document.getElementById('toc'); | ||||
| var dom_wrap = document.getElementById('mw'); | ||||
| var dom_hbar = document.getElementById('mh'); | ||||
| var dom_nav = document.getElementById('mn'); | ||||
| var dom_pre = document.getElementById('mp'); | ||||
| var dom_src = document.getElementById('mt'); | ||||
| var dom_navtgl = document.getElementById('navtoggle'); | ||||
| "use strict"; | ||||
|  | ||||
| var dom_toc = ebi('toc'); | ||||
| var dom_wrap = ebi('mw'); | ||||
| var dom_hbar = ebi('mh'); | ||||
| var dom_nav = ebi('mn'); | ||||
| var dom_pre = ebi('mp'); | ||||
| var dom_src = ebi('mt'); | ||||
| var dom_navtgl = ebi('navtoggle'); | ||||
|  | ||||
|  | ||||
| // chrome 49 needs this | ||||
| @@ -34,7 +36,7 @@ function cls(dom, name, add) { | ||||
| } | ||||
|  | ||||
|  | ||||
| function static(obj) { | ||||
| function statify(obj) { | ||||
|     return JSON.parse(JSON.stringify(obj)); | ||||
| } | ||||
|  | ||||
| @@ -158,6 +160,46 @@ function copydom(src, dst, lv) { | ||||
| } | ||||
|  | ||||
|  | ||||
| function md_plug_err(ex, js) { | ||||
|     var errbox = ebi('md_errbox'); | ||||
|     if (errbox) | ||||
|         errbox.parentNode.removeChild(errbox); | ||||
|  | ||||
|     if (!ex) | ||||
|         return; | ||||
|  | ||||
|     var msg = (ex + '').split('\n')[0]; | ||||
|     var ln = ex.lineNumber; | ||||
|     var o = null; | ||||
|     if (ln) { | ||||
|         msg = "Line " + ln + ", " + msg; | ||||
|         var lns = js.split('\n'); | ||||
|         if (ln < lns.length) { | ||||
|             o = document.createElement('span'); | ||||
|             o.style.cssText = 'color:#ac2;font-size:.9em;font-family:scp;display:block'; | ||||
|             o.textContent = lns[ln - 1]; | ||||
|         } | ||||
|     } | ||||
|     errbox = document.createElement('div'); | ||||
|     errbox.setAttribute('id', 'md_errbox'); | ||||
|     errbox.style.cssText = 'position:absolute;top:0;left:0;padding:1em .5em;background:#2b2b2b;color:#fc5' | ||||
|     errbox.textContent = msg; | ||||
|     errbox.onclick = function () { | ||||
|         alert('' + ex.stack); | ||||
|     }; | ||||
|     if (o) { | ||||
|         errbox.appendChild(o); | ||||
|         errbox.style.padding = '.25em .5em'; | ||||
|     } | ||||
|     dom_nav.appendChild(errbox); | ||||
|  | ||||
|     try { | ||||
|         console.trace(); | ||||
|     } | ||||
|     catch (ex2) { } | ||||
| } | ||||
|  | ||||
|  | ||||
| function load_plug(md_text, plug_type) { | ||||
|     if (!md_opt.allow_plugins) | ||||
|         return md_text; | ||||
| @@ -177,7 +219,14 @@ function load_plug(md_text, plug_type) { | ||||
|     var old_plug = md_plug[plug_type]; | ||||
|     if (!old_plug || old_plug[1] != js) { | ||||
|         js = 'const x = { ' + js + ' }; x;'; | ||||
|         var x = eval(js); | ||||
|         try { | ||||
|             var x = eval(js); | ||||
|         } | ||||
|         catch (ex) { | ||||
|             md_plug[plug_type] = null; | ||||
|             md_plug_err(ex, js); | ||||
|             return md; | ||||
|         } | ||||
|         if (x['ctor']) { | ||||
|             x['ctor'](); | ||||
|             delete x['ctor']; | ||||
| @@ -191,20 +240,30 @@ function load_plug(md_text, plug_type) { | ||||
|  | ||||
| function convert_markdown(md_text, dest_dom) { | ||||
|     md_text = md_text.replace(/\r/g, ''); | ||||
|  | ||||
|     md_plug_err(null); | ||||
|     md_text = load_plug(md_text, 'pre'); | ||||
|     md_text = load_plug(md_text, 'post'); | ||||
|  | ||||
|     marked.setOptions({ | ||||
|     var marked_opts = { | ||||
|         //headerPrefix: 'h-', | ||||
|         breaks: true, | ||||
|         gfm: true | ||||
|     }); | ||||
|     }; | ||||
|  | ||||
|     if (md_plug['pre']) { | ||||
|         marked.use(md_plug['pre'][0]); | ||||
|     var ext = md_plug['pre']; | ||||
|     if (ext) | ||||
|         Object.assign(marked_opts, ext[0]); | ||||
|  | ||||
|     try { | ||||
|         var md_html = marked(md_text, marked_opts); | ||||
|     } | ||||
|     catch (ex) { | ||||
|         if (ext) | ||||
|             md_plug_err(ex, ext[1]); | ||||
|  | ||||
|     var md_html = marked(md_text); | ||||
|         throw ex; | ||||
|     } | ||||
|     var md_dom = new DOMParser().parseFromString(md_html, "text/html").body; | ||||
|  | ||||
|     var nodes = md_dom.getElementsByTagName('a'); | ||||
| @@ -286,15 +345,29 @@ function convert_markdown(md_text, dest_dom) { | ||||
|         el.innerHTML = '<a href="#' + id + '">' + el.innerHTML + '</a>'; | ||||
|     } | ||||
|  | ||||
|     if (md_plug['post']) | ||||
|         md_plug['post'][0].render(md_dom); | ||||
|     ext = md_plug['post']; | ||||
|     if (ext && ext[0].render) | ||||
|         try { | ||||
|             ext[0].render(md_dom); | ||||
|         } | ||||
|         catch (ex) { | ||||
|             md_plug_err(ex, ext[1]); | ||||
|         } | ||||
|  | ||||
|     copydom(md_dom, dest_dom, 0); | ||||
|  | ||||
|     if (ext && ext[0].render2) | ||||
|         try { | ||||
|             ext[0].render2(dest_dom); | ||||
|         } | ||||
|         catch (ex) { | ||||
|             md_plug_err(ex, ext[1]); | ||||
|         } | ||||
| } | ||||
|  | ||||
|  | ||||
| function init_toc() { | ||||
|     var loader = document.getElementById('ml'); | ||||
|     var loader = ebi('ml'); | ||||
|     loader.parentNode.removeChild(loader); | ||||
|  | ||||
|     var anchors = [];  // list of toc entries, complex objects | ||||
|   | ||||
| @@ -77,32 +77,52 @@ html.dark #mt { | ||||
|     background: #f97; | ||||
|     border-radius: .15em; | ||||
| } | ||||
| html.dark #save.force-save { | ||||
|     color: #fca; | ||||
|     background: #720; | ||||
| } | ||||
| #save.disabled { | ||||
|     opacity: .4; | ||||
| } | ||||
| #helpbox, | ||||
| #toast { | ||||
|     background: #f7f7f7; | ||||
|     border-radius: .4em; | ||||
|     z-index: 9001; | ||||
| } | ||||
| #helpbox { | ||||
|     display: none; | ||||
|     position: fixed; | ||||
|     background: #f7f7f7; | ||||
|     box-shadow: 0 .5em 2em #777; | ||||
|     border-radius: .4em; | ||||
|     padding: 2em; | ||||
|     top: 4em; | ||||
|     overflow-y: auto; | ||||
|     box-shadow: 0 .5em 2em #777; | ||||
|     height: calc(100% - 12em); | ||||
|     left: calc(50% - 15em); | ||||
|     right: 0; | ||||
|     width: 30em; | ||||
|     z-index: 9001; | ||||
| } | ||||
| #helpclose { | ||||
|     display: block; | ||||
| } | ||||
| html.dark #helpbox { | ||||
|     background: #222; | ||||
|     box-shadow: 0 .5em 2em #444; | ||||
| } | ||||
| html.dark #helpbox, | ||||
| html.dark #toast { | ||||
|     background: #222; | ||||
|     border: 1px solid #079; | ||||
|     border-width: 1px 0; | ||||
| } | ||||
| #toast { | ||||
|     font-weight: bold; | ||||
|     text-align: center; | ||||
|     padding: .6em 0; | ||||
|     position: fixed; | ||||
|     z-index: 9001; | ||||
|     top: 30%; | ||||
|     transition: opacity 0.2s ease-in-out; | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| # mt {opacity: .5;top:1px} | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| "use strict"; | ||||
|  | ||||
|  | ||||
| // server state | ||||
| var server_md = dom_src.value; | ||||
|  | ||||
| @@ -8,15 +11,15 @@ var js_uni_whitelist = eval('\'' + esc_uni_whitelist + '\''); | ||||
|  | ||||
|  | ||||
| // dom nodes | ||||
| var dom_swrap = document.getElementById('mtw'); | ||||
| var dom_sbs = document.getElementById('sbs'); | ||||
| var dom_nsbs = document.getElementById('nsbs'); | ||||
| var dom_tbox = document.getElementById('toolsbox'); | ||||
| var dom_swrap = ebi('mtw'); | ||||
| var dom_sbs = ebi('sbs'); | ||||
| var dom_nsbs = ebi('nsbs'); | ||||
| var dom_tbox = ebi('toolsbox'); | ||||
| var dom_ref = (function () { | ||||
|     var d = document.createElement('div'); | ||||
|     d.setAttribute('id', 'mtr'); | ||||
|     dom_swrap.appendChild(d); | ||||
|     d = document.getElementById('mtr'); | ||||
|     d = ebi('mtr'); | ||||
|     // hide behind the textarea (offsetTop is not computed if display:none) | ||||
|     dom_src.style.zIndex = '4'; | ||||
|     d.style.zIndex = '3'; | ||||
| @@ -105,7 +108,7 @@ var draw_md = (function () { | ||||
|         map_src = genmap(dom_ref, map_src); | ||||
|         map_pre = genmap(dom_pre, map_pre); | ||||
|  | ||||
|         cls(document.getElementById('save'), 'disabled', src == server_md); | ||||
|         cls(ebi('save'), 'disabled', src == server_md); | ||||
|  | ||||
|         var t1 = new Date().getTime(); | ||||
|         delay = t1 - t0 > 100 ? 25 : 1; | ||||
| @@ -177,7 +180,7 @@ redraw = (function () { | ||||
|         y += src.clientHeight / 2; | ||||
|         var sy1 = -1, sy2 = -1, dy1 = -1, dy2 = -1; | ||||
|         for (var a = 1; a < nlines + 1; a++) { | ||||
|             if (srcmap[a] === null || dstmap[a] === null) | ||||
|             if (srcmap[a] == null || dstmap[a] == null) | ||||
|                 continue; | ||||
|  | ||||
|             if (srcmap[a] > y) { | ||||
| @@ -220,14 +223,108 @@ redraw = (function () { | ||||
| })(); | ||||
|  | ||||
|  | ||||
| // modification checker | ||||
| function Modpoll() { | ||||
|     this.skip_one = true; | ||||
|     this.disabled = false; | ||||
|  | ||||
|     this.periodic = function () { | ||||
|         var that = this; | ||||
|         setTimeout(function () { | ||||
|             that.periodic(); | ||||
|         }, 1000 * md_opt.modpoll_freq); | ||||
|  | ||||
|         var skip = null; | ||||
|  | ||||
|         if (ebi('toast')) | ||||
|             skip = 'toast'; | ||||
|  | ||||
|         else if (this.skip_one) | ||||
|             skip = 'saved'; | ||||
|  | ||||
|         else if (this.disabled) | ||||
|             skip = 'disabled'; | ||||
|  | ||||
|         if (skip) { | ||||
|             console.log('modpoll skip, ' + skip); | ||||
|             this.skip_one = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         console.log('modpoll...'); | ||||
|         var url = (document.location + '').split('?')[0] + '?raw&_=' + new Date().getTime(); | ||||
|         var xhr = new XMLHttpRequest(); | ||||
|         xhr.modpoll = this; | ||||
|         xhr.open('GET', url, true); | ||||
|         xhr.responseType = 'text'; | ||||
|         xhr.onreadystatechange = this.cb; | ||||
|         xhr.send(); | ||||
|     } | ||||
|  | ||||
|     this.cb = function () { | ||||
|         if (this.modpoll.disabled || this.modpoll.skip_one) { | ||||
|             console.log('modpoll abort'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this.readyState != XMLHttpRequest.DONE) | ||||
|             return; | ||||
|  | ||||
|         if (this.status !== 200) { | ||||
|             console.log('modpoll err ' + this.status + ": " + this.responseText); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!this.responseText) | ||||
|             return; | ||||
|  | ||||
|         var server_ref = server_md.replace(/\r/g, ''); | ||||
|         var server_now = this.responseText.replace(/\r/g, ''); | ||||
|  | ||||
|         if (server_ref != server_now) { | ||||
|             console.log("modpoll diff |" + server_ref.length + "|, |" + server_now.length + "|"); | ||||
|             this.modpoll.disabled = true; | ||||
|             var msg = [ | ||||
|                 "The document has changed on the server.<br />" + | ||||
|                 "The changes will NOT be loaded into your editor automatically.", | ||||
|  | ||||
|                 "Press F5 or CTRL-R to refresh the page,<br />" + | ||||
|                 "replacing your document with the server copy.", | ||||
|  | ||||
|                 "You can click this message to ignore and contnue." | ||||
|             ]; | ||||
|             return toast(false, "box-shadow:0 1em 2em rgba(64,64,64,0.8);font-weight:normal", | ||||
|                 36, "<p>" + msg.join('</p>\n<p>') + '</p>'); | ||||
|         } | ||||
|  | ||||
|         console.log('modpoll eq'); | ||||
|     } | ||||
|  | ||||
|     if (md_opt.modpoll_freq > 0) | ||||
|         this.periodic(); | ||||
|  | ||||
|     return this; | ||||
| }; | ||||
| var modpoll = new Modpoll(); | ||||
|  | ||||
|  | ||||
| window.onbeforeunload = function (e) { | ||||
|     if ((ebi("save").getAttribute('class') + '').indexOf('disabled') >= 0) | ||||
|         return; //nice (todo) | ||||
|  | ||||
|     e.preventDefault(); //ff | ||||
|     e.returnValue = ''; //chrome | ||||
| }; | ||||
|  | ||||
|  | ||||
| // save handler | ||||
| function save(e) { | ||||
|     if (e) e.preventDefault(); | ||||
|     var save_btn = document.getElementById("save"), | ||||
|     var save_btn = ebi("save"), | ||||
|         save_cls = save_btn.getAttribute('class') + ''; | ||||
|  | ||||
|     if (save_cls.indexOf('disabled') >= 0) { | ||||
|         toast('font-size:2em;color:#fc6;width:9em;', 'no changes'); | ||||
|         toast(true, ";font-size:2em;color:#c90", 9, "no changes"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -251,6 +348,8 @@ function save(e) { | ||||
|     xhr.onreadystatechange = save_cb; | ||||
|     xhr.btn = save_btn; | ||||
|     xhr.txt = txt; | ||||
|  | ||||
|     modpoll.skip_one = true;  // skip one iteration while we save | ||||
|     xhr.send(fd); | ||||
| } | ||||
|  | ||||
| @@ -344,23 +443,44 @@ function savechk_cb() { | ||||
|     last_modified = this.lastmod; | ||||
|     server_md = this.txt; | ||||
|     draw_md(); | ||||
|     toast('font-size:6em;font-family:serif;color:#cf6;width:4em;', | ||||
|     toast(true, ";font-size:6em;font-family:serif;color:#9b4", 4, | ||||
|         'OK✔️<span style="font-size:.2em;color:#999;position:absolute">' + this.ntry + '</span>'); | ||||
|  | ||||
|     modpoll.disabled = false; | ||||
| } | ||||
|  | ||||
| function toast(style, msg) { | ||||
|     var ok = document.createElement('div'); | ||||
|     style += 'font-weight:bold;background:#444;border-radius:.3em;padding:.6em 0;position:fixed;top:30%;left:calc(50% - 2em);text-align:center;z-index:9001;transition:opacity 0.2s ease-in-out;opacity:1'; | ||||
| function toast(autoclose, style, width, msg) { | ||||
|     var ok = ebi("toast"); | ||||
|     if (ok) | ||||
|         ok.parentNode.removeChild(ok); | ||||
|  | ||||
|     style = "width:" + width + "em;left:calc(50% - " + (width / 2) + "em);" + style; | ||||
|     ok = document.createElement('div'); | ||||
|     ok.setAttribute('id', 'toast'); | ||||
|     ok.setAttribute('style', style); | ||||
|     ok.innerHTML = msg; | ||||
|     var parent = document.getElementById('m'); | ||||
|     var parent = ebi('m'); | ||||
|     document.documentElement.appendChild(ok); | ||||
|     setTimeout(function () { | ||||
|         ok.style.opacity = 0; | ||||
|     }, 500); | ||||
|     setTimeout(function () { | ||||
|         ok.parentNode.removeChild(ok); | ||||
|     }, 750); | ||||
|  | ||||
|     var hide = function (delay) { | ||||
|         delay = delay || 0; | ||||
|  | ||||
|         setTimeout(function () { | ||||
|             ok.style.opacity = 0; | ||||
|         }, delay); | ||||
|  | ||||
|         setTimeout(function () { | ||||
|             if (ok.parentNode) | ||||
|                 ok.parentNode.removeChild(ok); | ||||
|         }, delay + 250); | ||||
|     } | ||||
|  | ||||
|     ok.onclick = function () { | ||||
|         hide(0); | ||||
|     }; | ||||
|  | ||||
|     if (autoclose) | ||||
|         hide(500); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -680,7 +800,8 @@ function fmt_table(e) { | ||||
|     for (var col = 0; col < ncols; col++) { | ||||
|         var max = 0; | ||||
|         for (var row = 0; row < tab.length; row++) | ||||
|             max = Math.max(max, tab[row][col].length); | ||||
|             if (row != 1) | ||||
|                 max = Math.max(max, tab[row][col].length); | ||||
|  | ||||
|         var s = ''; | ||||
|         for (var n = 0; n < max; n++) | ||||
| @@ -802,7 +923,7 @@ function cfg_uni(e) { | ||||
|             return false; | ||||
|         } | ||||
|         if (ev.code == "Escape" || kc == 27) { | ||||
|             var d = document.getElementById('helpclose'); | ||||
|             var d = ebi('helpclose'); | ||||
|             if (d) | ||||
|                 d.click(); | ||||
|         } | ||||
| @@ -859,22 +980,22 @@ function cfg_uni(e) { | ||||
|         } | ||||
|     } | ||||
|     document.onkeydown = keydown; | ||||
|     document.getElementById('save').onclick = save; | ||||
|     ebi('save').onclick = save; | ||||
| })(); | ||||
|  | ||||
|  | ||||
| document.getElementById('tools').onclick = function (e) { | ||||
| ebi('tools').onclick = function (e) { | ||||
|     if (e) e.preventDefault(); | ||||
|     var is_open = dom_tbox.getAttribute('class') != 'open'; | ||||
|     dom_tbox.setAttribute('class', is_open ? 'open' : ''); | ||||
| }; | ||||
|  | ||||
|  | ||||
| document.getElementById('help').onclick = function (e) { | ||||
| ebi('help').onclick = function (e) { | ||||
|     if (e) e.preventDefault(); | ||||
|     dom_tbox.setAttribute('class', ''); | ||||
|  | ||||
|     var dom = document.getElementById('helpbox'); | ||||
|     var dom = ebi('helpbox'); | ||||
|     var dtxt = dom.getElementsByTagName('textarea'); | ||||
|     if (dtxt.length > 0) { | ||||
|         convert_markdown(dtxt[0].value, dom); | ||||
| @@ -882,16 +1003,16 @@ document.getElementById('help').onclick = function (e) { | ||||
|     } | ||||
|  | ||||
|     dom.style.display = 'block'; | ||||
|     document.getElementById('helpclose').onclick = function () { | ||||
|     ebi('helpclose').onclick = function () { | ||||
|         dom.style.display = 'none'; | ||||
|     }; | ||||
| }; | ||||
|  | ||||
|  | ||||
| document.getElementById('fmt_table').onclick = fmt_table; | ||||
| document.getElementById('mark_uni').onclick = mark_uni; | ||||
| document.getElementById('iter_uni').onclick = iter_uni; | ||||
| document.getElementById('cfg_uni').onclick = cfg_uni; | ||||
| ebi('fmt_table').onclick = fmt_table; | ||||
| ebi('mark_uni').onclick = mark_uni; | ||||
| ebi('iter_uni').onclick = iter_uni; | ||||
| ebi('cfg_uni').onclick = cfg_uni; | ||||
|  | ||||
|  | ||||
| // blame steen | ||||
| @@ -999,9 +1120,9 @@ action_stack = (function () { | ||||
|         ref = newtxt; | ||||
|         dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length); | ||||
|         if (hist.un.length > 0) | ||||
|             dbg(static(hist.un.slice(-1)[0])); | ||||
|             dbg(statify(hist.un.slice(-1)[0])); | ||||
|         if (hist.re.length > 0) | ||||
|             dbg(static(hist.re.slice(-1)[0])); | ||||
|             dbg(statify(hist.re.slice(-1)[0])); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
| @@ -1015,7 +1136,7 @@ action_stack = (function () { | ||||
| })(); | ||||
|  | ||||
| /* | ||||
| document.getElementById('help').onclick = function () { | ||||
| ebi('help').onclick = function () { | ||||
|     var c1 = getComputedStyle(dom_src).cssText.split(';'); | ||||
|     var c2 = getComputedStyle(dom_ref).cssText.split(';'); | ||||
|     var max = Math.min(c1.length, c2.length); | ||||
|   | ||||
| @@ -25,7 +25,8 @@ | ||||
| var last_modified = {{ lastmod }}; | ||||
| var md_opt = { | ||||
| 	link_md_as_html: false, | ||||
| 	allow_plugins: {{ md_plug }} | ||||
| 	allow_plugins: {{ md_plug }}, | ||||
| 	modpoll_freq: {{ md_chk_rate }} | ||||
| }; | ||||
|  | ||||
| var lightswitch = (function () { | ||||
| @@ -42,6 +43,7 @@ var lightswitch = (function () { | ||||
| })(); | ||||
|  | ||||
| 	</script> | ||||
|     <script src="/.cpr/util.js"></script> | ||||
| 	<script src="/.cpr/deps/easymde.js"></script> | ||||
| 	<script src="/.cpr/mde.js"></script> | ||||
| </body></html> | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| var dom_wrap = document.getElementById('mw'); | ||||
| var dom_nav = document.getElementById('mn'); | ||||
| var dom_doc = document.getElementById('m'); | ||||
| var dom_md = document.getElementById('mt'); | ||||
| "use strict"; | ||||
|  | ||||
| var dom_wrap = ebi('mw'); | ||||
| var dom_nav = ebi('mn'); | ||||
| var dom_doc = ebi('m'); | ||||
| var dom_md = ebi('mt'); | ||||
|  | ||||
| (function () { | ||||
|     var n = document.location + ''; | ||||
| @@ -63,7 +65,7 @@ var mde = (function () { | ||||
|     mde.codemirror.on("change", function () { | ||||
|         md_changed(mde); | ||||
|     }); | ||||
|     var loader = document.getElementById('ml'); | ||||
|     var loader = ebi('ml'); | ||||
|     loader.parentNode.removeChild(loader); | ||||
|     return mde; | ||||
| })(); | ||||
| @@ -213,7 +215,7 @@ function save_chk() { | ||||
|     var ok = document.createElement('div'); | ||||
|     ok.setAttribute('style', 'font-size:6em;font-family:serif;font-weight:bold;color:#cf6;background:#444;border-radius:.3em;padding:.6em 0;position:fixed;top:30%;left:calc(50% - 2em);width:4em;text-align:center;z-index:9001;transition:opacity 0.2s ease-in-out;opacity:1'); | ||||
|     ok.innerHTML = 'OK✔️'; | ||||
|     var parent = document.getElementById('m'); | ||||
|     var parent = ebi('m'); | ||||
|     document.documentElement.appendChild(ok); | ||||
|     setTimeout(function () { | ||||
|         ok.style.opacity = 0; | ||||
|   | ||||
| @@ -1,61 +1,6 @@ | ||||
| "use strict"; | ||||
|  | ||||
| // error handler for mobile devices | ||||
| function hcroak(msg) { | ||||
|     document.body.innerHTML = msg; | ||||
|     window.onerror = undefined; | ||||
|     throw 'fatal_err'; | ||||
| } | ||||
| function croak(msg) { | ||||
|     document.body.textContent = msg; | ||||
|     window.onerror = undefined; | ||||
|     throw msg; | ||||
| } | ||||
| function esc(txt) { | ||||
|     return txt.replace(/[&"<>]/g, function (c) { | ||||
|         return { | ||||
|             '&': '&', | ||||
|             '"': '"', | ||||
|             '<': '<', | ||||
|             '>': '>' | ||||
|         }[c]; | ||||
|     }); | ||||
| } | ||||
| window.onerror = function (msg, url, lineNo, columnNo, error) { | ||||
|     window.onerror = undefined; | ||||
|     var html = ['<h1>you hit a bug!</h1><p>please screenshot this error and send me a copy arigathanks gozaimuch (ed/irc.rizon.net or ed#2644)</p><p>', | ||||
|         esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>']; | ||||
|  | ||||
|     if (error) { | ||||
|         var find = ['desc', 'stack', 'trace']; | ||||
|         for (var a = 0; a < find.length; a++) | ||||
|             if (String(error[find[a]]) !== 'undefined') | ||||
|                 html.push('<h2>' + find[a] + '</h2>' + | ||||
|                     esc(String(error[find[a]])).replace(/\n/g, '<br />\n')); | ||||
|     } | ||||
|     document.body.style.fontSize = '0.8em'; | ||||
|     document.body.style.padding = '0 1em 1em 1em'; | ||||
|     hcroak(html.join('\n')); | ||||
| }; | ||||
|  | ||||
|  | ||||
| // https://stackoverflow.com/a/950146 | ||||
| function import_js(url, cb) { | ||||
|     var head = document.head || document.getElementsByTagName('head')[0]; | ||||
|     var script = document.createElement('script'); | ||||
|     script.type = 'text/javascript'; | ||||
|     script.src = url; | ||||
|  | ||||
|     script.onreadystatechange = cb; | ||||
|     script.onload = cb; | ||||
|  | ||||
|     head.appendChild(script); | ||||
| } | ||||
|  | ||||
|  | ||||
| function o(id) { | ||||
|     return document.getElementById(id); | ||||
| } | ||||
| window.onerror = vis_exh; | ||||
|  | ||||
|  | ||||
| (function () { | ||||
| @@ -93,7 +38,7 @@ function goto(dest) { | ||||
|         obj[a].classList.remove('act'); | ||||
|  | ||||
|     if (dest) { | ||||
|         document.getElementById('op_' + dest).classList.add('act'); | ||||
|         ebi('op_' + dest).classList.add('act'); | ||||
|         document.querySelector('#ops>a[data-dest=' + dest + ']').classList.add('act'); | ||||
|  | ||||
|         var fn = window['goto_' + dest]; | ||||
| @@ -121,7 +66,7 @@ function goto_up2k() { | ||||
|         if (op !== null && op !== '.') | ||||
|             goto(op); | ||||
|     } | ||||
|     document.getElementById('ops').style.display = 'block'; | ||||
|     ebi('ops').style.display = 'block'; | ||||
| })(); | ||||
|  | ||||
|  | ||||
| @@ -150,21 +95,21 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|     // show modal message | ||||
|     function showmodal(msg) { | ||||
|         o('u2notbtn').innerHTML = msg; | ||||
|         o('u2btn').style.display = 'none'; | ||||
|         o('u2notbtn').style.display = 'block'; | ||||
|         o('u2conf').style.opacity = '0.5'; | ||||
|         ebi('u2notbtn').innerHTML = msg; | ||||
|         ebi('u2btn').style.display = 'none'; | ||||
|         ebi('u2notbtn').style.display = 'block'; | ||||
|         ebi('u2conf').style.opacity = '0.5'; | ||||
|     } | ||||
|  | ||||
|     // hide modal message | ||||
|     function unmodal() { | ||||
|         o('u2notbtn').style.display = 'none'; | ||||
|         o('u2btn').style.display = 'block'; | ||||
|         o('u2conf').style.opacity = '1'; | ||||
|         o('u2notbtn').innerHTML = ''; | ||||
|         ebi('u2notbtn').style.display = 'none'; | ||||
|         ebi('u2btn').style.display = 'block'; | ||||
|         ebi('u2conf').style.opacity = '1'; | ||||
|         ebi('u2notbtn').innerHTML = ''; | ||||
|     } | ||||
|  | ||||
|     var post_url = o('op_bup').getElementsByTagName('form')[0].getAttribute('action'); | ||||
|     var post_url = ebi('op_bup').getElementsByTagName('form')[0].getAttribute('action'); | ||||
|     if (post_url && post_url.charAt(post_url.length - 1) !== '/') | ||||
|         post_url += '/'; | ||||
|  | ||||
| @@ -181,25 +126,25 @@ function up2k_init(have_crypto) { | ||||
|             import_js('/.cpr/deps/sha512.js', unmodal); | ||||
|  | ||||
|             if (is_https) | ||||
|                 o('u2foot').innerHTML = shame + ' so <em>this</em> uploader will do like 500kB/s at best'; | ||||
|                 ebi('u2foot').innerHTML = shame + ' so <em>this</em> uploader will do like 500kB/s at best'; | ||||
|             else | ||||
|                 o('u2foot').innerHTML = 'seems like ' + shame + ' so do that if you want more performance'; | ||||
|                 ebi('u2foot').innerHTML = 'seems like ' + shame + ' so do that if you want more performance'; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     // show uploader if the user only has write-access | ||||
|     if (!o('files')) | ||||
|     if (!ebi('files')) | ||||
|         goto('up2k'); | ||||
|  | ||||
|     // shows or clears an error message in the basic uploader ui | ||||
|     function setmsg(msg) { | ||||
|         if (msg !== undefined) { | ||||
|             o('u2err').setAttribute('class', 'err'); | ||||
|             o('u2err').innerHTML = msg; | ||||
|             ebi('u2err').setAttribute('class', 'err'); | ||||
|             ebi('u2err').innerHTML = msg; | ||||
|         } | ||||
|         else { | ||||
|             o('u2err').setAttribute('class', ''); | ||||
|             o('u2err').innerHTML = ''; | ||||
|             ebi('u2err').setAttribute('class', ''); | ||||
|             ebi('u2err').innerHTML = ''; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -210,7 +155,7 @@ function up2k_init(have_crypto) { | ||||
|     } | ||||
|  | ||||
|     // handle user intent to use the basic uploader instead | ||||
|     o('u2nope').onclick = function (e) { | ||||
|     ebi('u2nope').onclick = function (e) { | ||||
|         e.preventDefault(); | ||||
|         setmsg(''); | ||||
|         goto('bup'); | ||||
| @@ -229,9 +174,9 @@ function up2k_init(have_crypto) { | ||||
|     function cfg_get(name) { | ||||
|         var val = localStorage.getItem(name); | ||||
|         if (val === null) | ||||
|             return parseInt(o(name).value); | ||||
|             return parseInt(ebi(name).value); | ||||
|  | ||||
|         o(name).value = val; | ||||
|         ebi(name).value = val; | ||||
|         return val; | ||||
|     } | ||||
|  | ||||
| @@ -242,7 +187,7 @@ function up2k_init(have_crypto) { | ||||
|         else | ||||
|             val = (val == '1'); | ||||
|  | ||||
|         o(name).checked = val; | ||||
|         ebi(name).checked = val; | ||||
|         return val; | ||||
|     } | ||||
|  | ||||
| @@ -250,7 +195,7 @@ function up2k_init(have_crypto) { | ||||
|         localStorage.setItem( | ||||
|             name, val ? '1' : '0'); | ||||
|  | ||||
|         o(name).checked = val; | ||||
|         ebi(name).checked = val; | ||||
|         return val; | ||||
|     } | ||||
|  | ||||
| @@ -284,9 +229,9 @@ function up2k_init(have_crypto) { | ||||
|         return un2k("this is the basic uploader; up2k needs at least<br />chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1"); | ||||
|  | ||||
|     function nav() { | ||||
|         o('file' + fdom_ctr).click(); | ||||
|         ebi('file' + fdom_ctr).click(); | ||||
|     } | ||||
|     o('u2btn').addEventListener('click', nav, false); | ||||
|     ebi('u2btn').addEventListener('click', nav, false); | ||||
|  | ||||
|     function ondrag(ev) { | ||||
|         ev.stopPropagation(); | ||||
| @@ -294,8 +239,8 @@ function up2k_init(have_crypto) { | ||||
|         ev.dataTransfer.dropEffect = 'copy'; | ||||
|         ev.dataTransfer.effectAllowed = 'copy'; | ||||
|     } | ||||
|     o('u2btn').addEventListener('dragover', ondrag, false); | ||||
|     o('u2btn').addEventListener('dragenter', ondrag, false); | ||||
|     ebi('u2btn').addEventListener('dragover', ondrag, false); | ||||
|     ebi('u2btn').addEventListener('dragenter', ondrag, false); | ||||
|  | ||||
|     function gotfile(ev) { | ||||
|         ev.stopPropagation(); | ||||
| @@ -357,7 +302,7 @@ function up2k_init(have_crypto) { | ||||
|             var tr = document.createElement('tr'); | ||||
|             tr.innerHTML = '<td id="f{0}n"></td><td id="f{0}t">hashing</td><td id="f{0}p" class="prog"></td>'.format(st.files.length); | ||||
|             tr.getElementsByTagName('td')[0].textContent = entry.name; | ||||
|             o('u2tab').appendChild(tr); | ||||
|             ebi('u2tab').appendChild(tr); | ||||
|  | ||||
|             st.files.push(entry); | ||||
|             st.todo.hash.push(entry); | ||||
| @@ -374,14 +319,14 @@ function up2k_init(have_crypto) { | ||||
|             alert(msg); | ||||
|         } | ||||
|     } | ||||
|     o('u2btn').addEventListener('drop', gotfile, false); | ||||
|     ebi('u2btn').addEventListener('drop', gotfile, false); | ||||
|  | ||||
|     function more_one_file() { | ||||
|         fdom_ctr++; | ||||
|         var elm = document.createElement('div') | ||||
|         elm.innerHTML = '<input id="file{0}" type="file" name="file{0}[]" multiple="multiple" />'.format(fdom_ctr); | ||||
|         o('u2form').appendChild(elm); | ||||
|         o('file' + fdom_ctr).addEventListener('change', gotfile, false); | ||||
|         ebi('u2form').appendChild(elm); | ||||
|         ebi('file' + fdom_ctr).addEventListener('change', gotfile, false); | ||||
|     } | ||||
|     more_one_file(); | ||||
|  | ||||
| @@ -602,7 +547,7 @@ function up2k_init(have_crypto) { | ||||
|             pb_html += '<div id="f{0}p{1}" style="width:{2}%"><div></div></div>'.format( | ||||
|                 t.n, a, pb_perc); | ||||
|  | ||||
|         o('f{0}p'.format(t.n)).innerHTML = pb_html; | ||||
|         ebi('f{0}p'.format(t.n)).innerHTML = pb_html; | ||||
|  | ||||
|         var reader = new FileReader(); | ||||
|  | ||||
| @@ -677,7 +622,7 @@ function up2k_init(have_crypto) { | ||||
|                 alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n')); | ||||
|             } | ||||
|  | ||||
|             o('f{0}t'.format(t.n)).innerHTML = 'connecting'; | ||||
|             ebi('f{0}t'.format(t.n)).innerHTML = 'connecting'; | ||||
|             st.busy.hash.splice(st.busy.hash.indexOf(t), 1); | ||||
|             st.todo.handshake.push(t); | ||||
|         }; | ||||
| @@ -706,7 +651,7 @@ function up2k_init(have_crypto) { | ||||
|                 if (response.name !== t.name) { | ||||
|                     // file exists; server renamed us | ||||
|                     t.name = response.name; | ||||
|                     o('f{0}n'.format(t.n)).textContent = t.name; | ||||
|                     ebi('f{0}n'.format(t.n)).textContent = t.name; | ||||
|                 } | ||||
|  | ||||
|                 t.postlist = []; | ||||
| @@ -736,13 +681,13 @@ function up2k_init(have_crypto) { | ||||
|                     msg = 'uploading'; | ||||
|                     done = false; | ||||
|                 } | ||||
|                 o('f{0}t'.format(t.n)).innerHTML = msg; | ||||
|                 ebi('f{0}t'.format(t.n)).innerHTML = msg; | ||||
|                 st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1); | ||||
|  | ||||
|                 if (done) { | ||||
|                     var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); | ||||
|                     var spd2 = (t.size / ((t.t3 - t.t2) / 1000.)) / (1024 * 1024.); | ||||
|                     o('f{0}p'.format(t.n)).innerHTML = 'hash {0}, up {1} MB/s'.format( | ||||
|                     ebi('f{0}p'.format(t.n)).innerHTML = 'hash {0}, up {1} MB/s'.format( | ||||
|                         spd1.toFixed(2), spd2.toFixed(2)); | ||||
|                 } | ||||
|                 tasker(); | ||||
| @@ -803,7 +748,7 @@ function up2k_init(have_crypto) { | ||||
|                     t.postlist.splice(t.postlist.indexOf(npart), 1); | ||||
|                     if (t.postlist.length == 0) { | ||||
|                         t.t3 = new Date().getTime(); | ||||
|                         o('f{0}t'.format(t.n)).innerHTML = 'verifying'; | ||||
|                         ebi('f{0}t'.format(t.n)).innerHTML = 'verifying'; | ||||
|                         st.todo.handshake.push(t); | ||||
|                     } | ||||
|                     tasker(); | ||||
| @@ -834,7 +779,7 @@ function up2k_init(have_crypto) { | ||||
|     // | ||||
|  | ||||
|     function prog(nfile, nchunk, color, percent) { | ||||
|         var n1 = o('f{0}p{1}'.format(nfile, nchunk)); | ||||
|         var n1 = ebi('f{0}p{1}'.format(nfile, nchunk)); | ||||
|         var n2 = n1.getElementsByTagName('div')[0]; | ||||
|         if (percent === undefined) { | ||||
|             n1.style.background = color; | ||||
| @@ -857,7 +802,7 @@ function up2k_init(have_crypto) { | ||||
|             dir.preventDefault(); | ||||
|         } catch (ex) { } | ||||
|  | ||||
|         var obj = o('nthread'); | ||||
|         var obj = ebi('nthread'); | ||||
|         if (dir.target) { | ||||
|             obj.style.background = '#922'; | ||||
|             var v = Math.floor(parseInt(obj.value)); | ||||
| @@ -892,19 +837,19 @@ function up2k_init(have_crypto) { | ||||
|         this.click(); | ||||
|     } | ||||
|  | ||||
|     o('nthread_add').onclick = function (ev) { | ||||
|     ebi('nthread_add').onclick = function (ev) { | ||||
|         ev.preventDefault(); | ||||
|         bumpthread(1); | ||||
|     }; | ||||
|     o('nthread_sub').onclick = function (ev) { | ||||
|     ebi('nthread_sub').onclick = function (ev) { | ||||
|         ev.preventDefault(); | ||||
|         bumpthread(-1); | ||||
|     }; | ||||
|  | ||||
|     o('nthread').addEventListener('input', bumpthread, false); | ||||
|     o('multitask').addEventListener('click', tgl_multitask, false); | ||||
|     ebi('nthread').addEventListener('input', bumpthread, false); | ||||
|     ebi('multitask').addEventListener('click', tgl_multitask, false); | ||||
|  | ||||
|     var nodes = o('u2conf').getElementsByTagName('a'); | ||||
|     var nodes = ebi('u2conf').getElementsByTagName('a'); | ||||
|     for (var a = nodes.length - 1; a >= 0; a--) | ||||
|         nodes[a].addEventListener('touchend', nop, false); | ||||
|  | ||||
|   | ||||
							
								
								
									
										109
									
								
								copyparty/web/util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								copyparty/web/util.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| "use strict"; | ||||
|  | ||||
| // error handler for mobile devices | ||||
| function hcroak(msg) { | ||||
|     document.body.innerHTML = msg; | ||||
|     window.onerror = undefined; | ||||
|     throw 'fatal_err'; | ||||
| } | ||||
| function croak(msg) { | ||||
|     document.body.textContent = msg; | ||||
|     window.onerror = undefined; | ||||
|     throw msg; | ||||
| } | ||||
| function esc(txt) { | ||||
|     return txt.replace(/[&"<>]/g, function (c) { | ||||
|         return { | ||||
|             '&': '&', | ||||
|             '"': '"', | ||||
|             '<': '<', | ||||
|             '>': '>' | ||||
|         }[c]; | ||||
|     }); | ||||
| } | ||||
| function vis_exh(msg, url, lineNo, columnNo, error) { | ||||
|     window.onerror = undefined; | ||||
|     var html = ['<h1>you hit a bug!</h1><p>please screenshot this error and send me a copy arigathanks gozaimuch (ed/irc.rizon.net or ed#2644)</p><p>', | ||||
|         esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>']; | ||||
|  | ||||
|     if (error) { | ||||
|         var find = ['desc', 'stack', 'trace']; | ||||
|         for (var a = 0; a < find.length; a++) | ||||
|             if (String(error[find[a]]) !== 'undefined') | ||||
|                 html.push('<h2>' + find[a] + '</h2>' + | ||||
|                     esc(String(error[find[a]])).replace(/\n/g, '<br />\n')); | ||||
|     } | ||||
|     document.body.style.fontSize = '0.8em'; | ||||
|     document.body.style.padding = '0 1em 1em 1em'; | ||||
|     hcroak(html.join('\n')); | ||||
| }; | ||||
|  | ||||
|  | ||||
| function ebi(id) { | ||||
|     return document.getElementById(id); | ||||
| } | ||||
|  | ||||
|  | ||||
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith | ||||
| if (!String.prototype.endsWith) { | ||||
|     String.prototype.endsWith = function (search, this_len) { | ||||
|         if (this_len === undefined || this_len > this.length) { | ||||
|             this_len = this.length; | ||||
|         } | ||||
|         return this.substring(this_len - search.length, this_len) === search; | ||||
|     }; | ||||
| } | ||||
| if (!String.startsWith) { | ||||
|     String.prototype.startsWith = function (s, i) { | ||||
|         i = i > 0 ? i | 0 : 0; | ||||
|         return this.substring(i, i + s.length) === s; | ||||
|     }; | ||||
| } | ||||
|  | ||||
|  | ||||
| // https://stackoverflow.com/a/950146 | ||||
| function import_js(url, cb) { | ||||
|     var head = document.head || document.getElementsByTagName('head')[0]; | ||||
|     var script = document.createElement('script'); | ||||
|     script.type = 'text/javascript'; | ||||
|     script.src = url; | ||||
|  | ||||
|     script.onreadystatechange = cb; | ||||
|     script.onload = cb; | ||||
|  | ||||
|     head.appendChild(script); | ||||
| } | ||||
|  | ||||
|  | ||||
| function sortTable(table, col) { | ||||
|     var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows | ||||
|         th = table.tHead.rows[0].cells, | ||||
|         tr = Array.prototype.slice.call(tb.rows, 0), | ||||
|         i, reverse = th[col].className == 'sort1' ? -1 : 1; | ||||
|     for (var a = 0, thl = th.length; a < thl; a++) | ||||
|         th[a].className = ''; | ||||
|     th[col].className = 'sort' + reverse; | ||||
|     var stype = th[col].getAttribute('sort'); | ||||
|     tr = tr.sort(function (a, b) { | ||||
|         var v1 = a.cells[col].textContent.trim(); | ||||
|         var v2 = b.cells[col].textContent.trim(); | ||||
|         if (stype == 'int') { | ||||
|             v1 = parseInt(v1.replace(/,/g, '')); | ||||
|             v2 = parseInt(v2.replace(/,/g, '')); | ||||
|             return reverse * (v1 - v2); | ||||
|         } | ||||
|         return reverse * (v1.localeCompare(v2)); | ||||
|     }); | ||||
|     for (i = 0; i < tr.length; ++i) tb.appendChild(tr[i]); | ||||
| } | ||||
| function makeSortable(table) { | ||||
|     var th = table.tHead, i; | ||||
|     th && (th = th.rows[0]) && (th = th.cells); | ||||
|     if (th) i = th.length; | ||||
|     else return; // if no `<thead>` then do nothing | ||||
|     while (--i >= 0) (function (i) { | ||||
|         th[i].onclick = function () { | ||||
|             sortTable(table, i); | ||||
|         }; | ||||
|     }(i)); | ||||
| } | ||||
| @@ -19,6 +19,7 @@ set -e | ||||
| # -rwxr-xr-x  0 ed ed  183808 Nov 19 00:43 copyparty-extras/sfx-lite/copyparty-sfx.py | ||||
|  | ||||
|  | ||||
| command -v gtar && tar=gtar || tar=tar | ||||
| td="$(mktemp -d)" | ||||
| od="$(pwd)" | ||||
| cd "$td" | ||||
| @@ -28,7 +29,7 @@ pwd | ||||
| # debug: if cache exists, use that instead of bothering github | ||||
| cache="$od/.copyparty-repack.cache" | ||||
| [ -e "$cache" ] && | ||||
| 	tar -xvf "$cache" || | ||||
| 	$tar -xvf "$cache" || | ||||
| { | ||||
| 	# get download links from github | ||||
| 	curl https://api.github.com/repos/9001/copyparty/releases/latest | | ||||
| @@ -43,7 +44,7 @@ cache="$od/.copyparty-repack.cache" | ||||
| 	tr -d '\r' | tr '\n' '\0' | xargs -0 curl -L --remote-name-all | ||||
|  | ||||
| 	# debug: create cache | ||||
| 	#tar -czvf "$cache" * | ||||
| 	#$tar -czvf "$cache" * | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -56,7 +57,7 @@ mv copyparty-*.tar.gz copyparty-extras/ | ||||
|  | ||||
| # unpack the source code | ||||
| ( cd copyparty-extras/ | ||||
| tar -xvf *.tar.gz | ||||
| $tar -xvf *.tar.gz | ||||
| ) | ||||
|  | ||||
|  | ||||
| @@ -92,7 +93,7 @@ cp -pv "$od/$0" copyparty-extras/ | ||||
|  | ||||
| # create the bundle | ||||
| fn=copyparty-$(date +%Y-%m%d-%H%M%S).tgz | ||||
| tar -czvf "$od/$fn" * | ||||
| $tar -czvf "$od/$fn" * | ||||
| cd "$od" | ||||
| rm -rf "$td" | ||||
|  | ||||
|   | ||||
| @@ -94,8 +94,39 @@ cd sfx | ||||
| 	rm -f ../tar | ||||
| } | ||||
|  | ||||
| ver="$(awk '/^VERSION *= \(/ { | ||||
| 	gsub(/[^0-9,]/,""); gsub(/,/,"."); print; exit}' < ../copyparty/__version__.py)" | ||||
| ver= | ||||
| command -v git >/dev/null && { | ||||
| 	git_ver="$(git describe --tags)";  # v0.5.5-2-gb164aa0 | ||||
| 	ver="$(printf '%s\n' "$git_ver" | sed -r 's/^v//; s/-g?/./g')"; | ||||
| 	t_ver= | ||||
|  | ||||
| 	printf '%s\n' "$git_ver" | grep -qE '^v[0-9\.]+$' && { | ||||
| 		# short format (exact version number) | ||||
| 		t_ver="$(printf '%s\n' "$ver" | sed -r 's/\./, /g')"; | ||||
| 	} | ||||
|  | ||||
| 	printf '%s\n' "$git_ver" | grep -qE '^v[0-9\.]+-[0-9]+-g[0-9a-f]+$' && { | ||||
| 		# long format (unreleased commit) | ||||
| 		t_ver="$(printf '%s\n' "$ver" | sed -r 's/\./, /g; s/(.*) (.*)/\1 "\2"/')" | ||||
| 	} | ||||
|  | ||||
| 	[ -z "$t_ver" ] && { | ||||
| 		printf 'unexpected git version format: [%s]\n' "$git_ver" | ||||
| 		exit 1 | ||||
| 	} | ||||
|  | ||||
| 	dt="$(git log -1 --format=%cd --date=format:'%Y, %m, %d')" | ||||
| 	printf 'git %3s: \033[36m%s\033[0m\n' ver "$ver" dt "$dt" | ||||
| 	sed -ri ' | ||||
| 		s/^(VERSION =)(.*)/#\1\2\n\1 ('"$t_ver"')/; | ||||
| 		s/^(S_VERSION =)(.*)/#\1\2\n\1 "'"$ver"'"/; | ||||
| 		s/^(BUILD_DT =)(.*)/#\1\2\n\1 ('"$dt"')/; | ||||
| 	' copyparty/__version__.py | ||||
| } | ||||
|  | ||||
| [ -z "$ver" ] &&  | ||||
| 	ver="$(awk '/^VERSION *= \(/ { | ||||
| 		gsub(/[^0-9,]/,""); gsub(/,/,"."); print; exit}' < copyparty/__version__.py)" | ||||
|  | ||||
| ts=$(date -u +%s) | ||||
| hts=$(date -u +%Y-%m%d-%H%M%S) # --date=@$ts (thx osx) | ||||
|   | ||||
| @@ -18,6 +18,8 @@ this one becomes a hyperlink to ./except/ thanks to | ||||
|  | ||||
| it is a passthrough to the markdown extension api, see https://marked.js.org/using_pro | ||||
|  | ||||
| in addition to the markdown extension functions, `ctor` will be called on document init | ||||
|  | ||||
|  | ||||
| ### these/ | ||||
| and this one becomes ./except/these/ | ||||
| @@ -36,6 +38,13 @@ whic hshoud be ./except/also-this.md | ||||
| # ok | ||||
| now for another extension type, `copyparty_post` which is called to manipulate the generated dom instead | ||||
|  | ||||
| `copyparty_post` can have the following functions, all optional | ||||
| * `ctor` is called on document init | ||||
| * `render` is called when the dom is done but still in-memory | ||||
| * `render2` is called with the live browser dom as-displayed | ||||
|  | ||||
| ## post example | ||||
|  | ||||
| the values in the `ex:` columns are linkified to `example.com/$value` | ||||
|  | ||||
| | ex:foo       | bar      | ex:baz | | ||||
| @@ -43,6 +52,8 @@ the values in the `ex:` columns are linkified to `example.com/$value` | ||||
| | asdf         | nice     | fgsfds | | ||||
| | more one row | hi hello | aaa    | | ||||
|  | ||||
| and the table can be sorted by clicking the headers | ||||
|  | ||||
| the difference is that with `copyparty_pre` you'll probably break various copyparty features but if you use `copyparty_post` then future copyparty versions will probably break you | ||||
|  | ||||
|  | ||||
| @@ -123,5 +134,8 @@ render(dom) { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }, | ||||
| render2(dom) { | ||||
|     window.makeSortable(dom.getElementsByTagName('table')[0]); | ||||
| } | ||||
| ``` | ||||
|   | ||||
		Reference in New Issue
	
	Block a user