parsav  Diff

Differences From Artifact [9100f46c08]:

To Artifact [f01ba9ae01]:


     2      2    * if there are any UI elements unfortunate enough to need
     3      3    * interactivity beyond what native HTML+CSS can provide. if so,
     4      4    * we attach the appropriate listeners to them. */
     5      5   window.addEventListener('load', function() {
     6      6   	/* social media is less fun when you can't just click on a tweet
     7      7   	 * to insta-like or -retweet it. this is unfortunately not possible
     8      8   	 * (except in various hideously shitty ways) without javascript. */
     9         -	function mk(elt) { return document.createElement(elt); }
    10         -	function posturl(post) {
    11         -		return post.querySelector('.permalink').attributes.getNamedItem('href').value;
    12         -	}
            9  +	let mk      =  elt => document.createElement(elt);
           10  +	let posturl = post => post.querySelector('.permalink').attributes.getNamedItem('href').value;
           11  +	let focused =   () => document.querySelector('textarea:focus, input:focus, button:focus, select:focus, a[href]:focus') != null;
    13     12   	function postReq(url,act,elt) {
    14     13   		fetch(new Request(url, {
    15     14   			method: 'POST',
    16     15   			body: 'act='+act
    17     16   		})).then(function(resp) {
    18     17   			if (resp.ok && resp.status == 200) {
    19         -				var i = parseInt(elt.innerHTML)
           18  +				let i = parseInt(elt.innerHTML)
    20     19   				if (isNaN(i)) {i=0}
    21     20   				elt.innerHTML = (i+1).toString()
           21  +				elt.animate({
           22  +					transform: ['scale(1.4)', 'scale(1.0)'],
           23  +					filter: ['brightness(1)','brightness(0)']
           24  +				},200)
    22     25   			}
    23     26   		})
    24     27   	}
    25     28   
    26         -	/* div-based like and rt aren't very keyboard-friendly. add a replacement */
    27         -	if (document.querySelector('body.timeline, body.profile') != null) {
    28         -		window.addEventListener('keydown', function(event) {
           29  +	function onkey(elt, fn) {
           30  +		elt.addEventListener('keydown', function(ev) {
    29     31   			if (!window._liveTweetMap) { return; }
    30     32   			if (event.isComposing || event.keyCode === 229) { return; } // 🙄
    31         -			var cururl = window._liveTweetMap.cur;
    32         -			var nexturl = null;
           33  +			return fn(ev);
           34  +		})
           35  +	}
           36  +
           37  +	/* div-based like and rt aren't very keyboard-friendly. add a replacement */
           38  +	if (document.querySelector('body.timeline, body.profile, body.post') != null) {
           39  +		onkey(window, function(event) {
           40  +			if (focused()) {return;}
           41  +			let cururl = window._liveTweetMap.cur;
           42  +			let nexturl = null;
    33     43   			if (event.key == 'j') { // down
    34     44   				if (cururl == null) {
    35     45   					nexturl = window._liveTweetMap.first
    36     46   				} else {
    37     47   					nexturl = window._liveTweetMap.map.get(cururl).next
    38     48   				}
    39     49   			} else if (event.key == 'k') { // up
    40     50   				if (cururl == null) {
    41     51   					nexturl = window._liveTweetMap.last
    42     52   				} else {
    43     53   					nexturl = window._liveTweetMap.map.get(cururl).prev
    44     54   				}
    45     55   			} else if (cururl != null) {
    46         -				var post = window._liveTweetMap.map.get(cururl).me
           56  +				let post = window._liveTweetMap.map.get(cururl).me
    47     57   				if (event.key == 'f') { // fave
    48     58   					postReq(cururl, 'like', post.querySelector('.stats>.like'))
    49     59   				} else if (event.key == 'r') { // rt
    50     60   					postReq(cururl, 'rt', post.querySelector('.stats>.rt'))
           61  +				} else if (event.key == 'd') { // rt
           62  +					if (post.attributes.getNamedItem('data-own')) {
           63  +						window.location = cururl + '/del';
           64  +					}
    51     65   				} else if (event.key == 'Enter') { // nav
    52     66   					window.location = cururl;
    53     67   					return;
    54     68   				}
    55     69   			}
    56     70   			if (nexturl != null) {
    57     71   				if (cururl != null) {
    58         -					var cur = window._liveTweetMap.map.get(cururl);
           72  +					let cur = window._liveTweetMap.map.get(cururl);
    59     73   					cur.me.classList.remove('live-selected')
    60     74   				}
    61         -				var next = window._liveTweetMap.map.get(nexturl);
           75  +				let next = window._liveTweetMap.map.get(nexturl);
    62     76   				next.me.classList.add('live-selected')
    63     77   				window._liveTweetMap.cur = nexturl
    64     78   			}
    65     79   		});
    66     80   	}
           81  +	
           82  +	/* make ctrl-enter submit poasts. why the fuck does this require jabbascript */
           83  +	document.querySelectorAll('form').forEach(form => form.querySelectorAll('textarea').forEach(function(te) {
           84  +		let submitbtn = form.querySelector('button[name], input[type="submit"][name], input[type="image"][name]');
           85  +		onkey(te, function(e) {
           86  +			if(e.ctrlKey && e.keyCode == 13) {
           87  +				if(submitbtn == null) { form.submit(); } else { submitbtn.click(); }
           88  +				// are you kidding me with this shit
           89  +				return true;
           90  +			}
           91  +		})
           92  +	}));
           93  +
           94  +	/* allow response to queries via the keyboard */
           95  +	let queryform = document.querySelector('body.query form');
           96  +	if(queryform != null) {
           97  +		okbtn = queryform.querySelector('button[name]');
           98  +		nobtn = queryform.querySelector('.button.no, button.no');
           99  +		onkey(window, function(e) {
          100  +			if (focused()) {return;}
          101  +			if (e.keyCode == 13 || e.key == 'y') {
          102  +				if (okbtn != null) { okbtn.click() } else { queryform.submit() }
          103  +			} else if (e.key == 'Escape' || e.key == 'n') {
          104  +				if (nobtn != null) { nobtn.click() } else { window.history.back() }
          105  +			}
          106  +		});
          107  +	}
    67    108   
    68    109   	function attachButtons() {
    69         -		var last = null;
    70         -		var newmap = { cur: null, first: null, last: null, map: new Map() }
    71         -		document.querySelectorAll('body:not(.post) main article.post').forEach(function(post){
          110  +		let last = null;
          111  +		let newmap = { cur: null, first: null, last: null, map: new Map() }
          112  +		document.querySelectorAll('main article.post').forEach(function(post){
    72    113   			let url = posturl(post);
    73    114   			if (last == null) { newmap.first = url; } else {
    74    115   				newmap.map.get(last).next = url
    75    116   			}
    76    117   			newmap.map.set(url, {me: post, prev: last, next: null})
    77    118   			last = url
    78    119   			if (window._liveTweetMap && window._liveTweetMap.cur == url) {
    79    120   				post.classList.add('live-selected');
    80    121   			}
    81    122   
    82         -			var stats = post.querySelector('.stats');
          123  +			let stats = post.querySelector('.stats');
    83    124   			if (stats == null) {
    84    125   				/* no stats box; create one */
    85         -				var n = mk('div');
          126  +				let n = mk('div');
    86    127   				n.classList.add('stats');
    87    128   				post.appendChild(n);
    88    129   				stats = n
    89    130   			}
    90    131   			function ensureElt(cls, before) {
    91    132   				let s = stats.querySelector('.' + cls);
    92    133   				if (s == null) {
    93         -					var n = mk('div');
          134  +					let n = mk('div');
    94    135   					n.classList.add(cls);
    95    136   					if (before == null) { stats.appendChild(n) } else {
    96    137   						stats.insertBefore(n,stats.querySelector(before))
    97    138   					}
    98    139   					return n
    99    140   				} else { return s }
   100    141   			}
   101         -			var like = ensureElt('like', null);
   102         -			var rt   = ensureElt('rt','.like');
          142  +			let like = ensureElt('like', null);
          143  +			let rt   = ensureElt('rt','.like');
   103    144   			function activate(elt,name) {
   104    145   				elt.addEventListener('click', function(e) { postReq(url,name,elt) });
   105    146   				elt.style.setProperty('cursor','pointer');
   106    147   				elt.setAttribute('tabindex','0');
   107    148   			}
   108    149   			activate(like,'like');
   109    150   			activate(rt,'rt');
   110    151   		});
   111    152   		newmap.last = last
   112    153   		if (window._liveTweetMap) {
   113    154   			newmap.cur = window._liveTweetMap.cur // TODO handle vanishments
   114    155   		}
   115         -		window._liveTweetMap = newmap
          156  +		window._liveTweetMap = newmap;
   116    157   	}
   117    158   
   118    159   	/* update hue-picker background when slider is adjusted */
   119    160   	document.querySelectorAll('.color-picker').forEach(function(box) {
   120    161   		let slider = box.querySelector('[data-color-pick]');
   121    162   		box.style.setProperty('--hue', slider.value);
   122    163   		slider.addEventListener('input', function(e) {
................................................................................
   131    172   	 * tree from its html, find the element in question, ferret out
   132    173   	 * any deltas, and apply them. */
   133    174   	document.querySelectorAll('*[data-live]').forEach(function(container) {
   134    175   		let interv = parseFloat(container.attributes.getNamedItem('data-live').nodeValue) * 1000;
   135    176   		container._liveLastArrival = 0; /* TODO include initial value in document */
   136    177   
   137    178   		window.setInterval(function() {
   138         -			var req = new Request(window.location, {
          179  +			let req = new Request(window.location, {
   139    180   				method: 'GET',
   140         -				headers: {
   141         -					'X-Live-Last-Arrival': container._liveLastArrival
   142         -				}
          181  +				headers: { 'X-Live-Last-Arrival': container._liveLastArrival }
   143    182   			})
   144    183   			
   145    184   			fetch(req).then(function(resp) {
   146    185   				if (!resp.ok) return;
   147    186   				let newest = parseInt(resp.headers.get('X-Live-Newest-Artifact'));
   148    187   				if (newest == container._liveLastArrival) { // != also handles some deletions
   149    188   					resp.body.cancel();
   150    189   					return;
   151    190   				}
   152    191   				container._liveLastArrival = newest
   153    192   
   154    193   				resp.text().then(function(htmlbody) {
   155         -					var parser = new DOMParser();
   156         -					var newdoc = parser.parseFromString(htmlbody,'text/html')
          194  +					let parser = new DOMParser();
          195  +					let newdoc = parser.parseFromString(htmlbody,'text/html')
   157    196   					container.innerHTML = newdoc.getElementById(container.id).innerHTML;
   158    197   					attachButtons();
   159    198   				})
   160    199   			})
   161    200   		}, interv)
   162    201   	});
   163    202   
   164    203   	attachButtons();
   165    204   });