Differences From
Artifact [9100f46c08]:
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 });