s4

view s4-main.js @ 889:5843755e3b30

The beginning of AJAX operations Frozen/Running can be toggled by ajax button
author HIROSE Yuuji <yuuji@gentei.org>
date Thu, 24 Dec 2020 19:42:58 +0900
parents 3e09ac711f69
children c1a5d137740d
line source
1 // 愛
2 (function (){
3 function collectElementsByAttr(elm, attr, val) {
4 var e = document.getElementsByTagName(elm);
5 if (!e) return null;
6 var list = [];
7 for (var i of e) {
8 if (i.getAttribute(attr) == val)
9 list.push(i)
10 }
11 return list;
12 }
13 function nthChildOf(parent, n, elem) { // Return Nth child of type ELEM
14 // N begins with 1
15 var i=0;
16 var le = elem.toLowerCase();
17 for (var c of parent.childNodes) {
18 if (!c.tagName) continue;
19 if (c.tagName.toLowerCase() == le) {
20 if (++i >= n) return c;
21 }
22 }
23 return null;
24 }
25 function insertRedirect(e) {
26 var articleId, textarea = document.getElementById("text");
27 var p = e.target, checked = p.checked;
28 while (p = p.parentNode)
29 if (p.nodeName.match(/^td$/i)) break;
30 if (!p) return;
31 while (p = p.nextSibling)
32 if (p.nodeName.match(/^td$/i)) break;
33 if (!p) return;
34 articleId = p.getAttribute("id");
35 if (textarea && articleId) {
36 var tv = textarea.value, lines;
37 if (tv)
38 lines = tv.split("\n");
39 else
40 lines = [""];
41 var re = new RegExp("[, ]*#"+articleId+"(?![0-9])");
42 checked = (p.nodeName.match(/^input$/)
43 ? p.checked // checkbox obeys its status
44 : !lines[0].match(re)) // a-elment toggles redirection
45 if (checked) {
46 if (!lines[0].match(re)) {
47 var re2 = new RegExp(/>#[#0-9, ]+[0-9]/);
48 if (lines[0].match(re2))
49 lines[0] = lines[0].replace(
50 re2, '$&, '+'#'+articleId);
51 else {
52 if (lines[0] > "") lines[0] = " "+lines[0];
53 lines[0] = ">#"+articleId+lines[0];
54 }
55 }
56 } else { // Remove #xxxxx
57 if (lines[0].match(/^>#[0-9 ,]+#/)) // 2 or more #id's
58 lines[0] = lines[0].replace(
59 new RegExp("^>#"+articleId+"[ ,]*"), ">").replace(
60 new RegExp("[ ,]*#"+articleId), "");
61 else {
62 lines[0] = lines[0].replace(
63 new RegExp(">#"+articleId+"[ ,]*"), "");
64 }
65 }
66 lines[0] = lines[0].replace(/^> *$/, '');
67 textarea.value = lines.join("\n");
68 }
69 }
70 function reverseChecks() {
71 var names = collectElementsByAttr("input", "name", "usel");
72 for (let u of names) {
73 u.checked = !u.checked;
74 }
75 }
76 function renumberOL(str, start) {
77 var stra = str.split("\n");
79 for (var i=1; i<stra.length; i++) {
80 if (stra[i].match(/^[1-9][0-9]*\. /)) {
81 let orig=stra[i];
82 stra[i] = (++start)+". "+RegExp.rightContext;
83 } else if (stra[i].match(/^ /)) {
84 continue;
85 } else
86 break;
87 }
88 return stra.join("\n");
89 }
90 function submitThisForm(input) {
91 for (var elm=input.parentNode; elm; elm = elm.parentNode) {
92 if (elm.nodeName.match(/form/i)) {
93 elm.submit();
94 return true;
95 }
96 }
97 return false;
98 }
99 function helpMarkdownBS(e) {
100 var area = e.target, pos = area.selectionStart, text = area.value;
101 if (area.selectionStart != area.selectionEnd) return;
102 if (pos<2) return;
103 if (text.substr(pos-1, 2)=="\n\n") return;
104 var bol = text.lastIndexOf("\n", pos-1),
105 eol = text.indexOf("\n", pos);
106 if (bol<=0 || bol==eol) return;
107 var thisline = text.substring(bol+1, eol==-1 ? text.length : eol);
108 thisline = text.substring(bol+1, pos);
109 if (thisline == "* ") {
110 area.setSelectionRange(pos-2, pos);
111 } else if (thisline.match(/^[1-9][0-9]*\. $/)) {
112 area.setSelectionRange(pos-RegExp.lastMatch.length, pos);
113 }
114 }
115 function helpMarkdownEnter(e) {
116 if (e.keyCode == 13 && !e.shiftKey) {
117 if (e.metaKey && submitThisForm(e.target)) {
118 e.preventDefault();
119 return;
120 }
121 var area = e.target;
122 var pos = area.selectionStart, text = area.value;
123 if (pos==0) return;
124 var last = text.lastIndexOf("\n", pos-1);
125 var rest = text.substring(pos), rest0=rest;
126 var line = last ? text.substring(last+1, pos) : text;
127 var next = rest.substring(rest.indexOf("\n"))||rest;
128 next=next.substring(1);
129 var tail = text.substring(pos-2, pos), br = (tail==" ");
130 var add = "", offset = 1;
131 if (line.startsWith("* ")) {
132 add = "* ";
133 offset += add.length;
134 if (br) {
135 add = " " + "\n" + add;
136 }
137 } else if (line.match(/^([1-9][0-9]*)\. /)) {
138 var ln = parseInt(RegExp.$1), nn=ln+1,
139 len = RegExp.lastMatch.length;
140 add = nn+". ";
141 let toeol = text.substr(pos, text.indexOf("\n"));
142 if (br) {
143 if (next.startsWith(add)) {
144 add=" ".repeat(len);
145 nn = ln;
146 } else {
147 add = " ".repeat(len)+ "\n" + add;
148 offset -= len+1;
149 }
150 }
151 if (next.match(/^[1-9][0-9]*\. /))
152 rest = renumberOL(rest, nn);
153 offset += add.length;
154 } else if (line.match(/^\|( *).+\|/)) {
155 add = "|" + RegExp.$1 + " |";
156 offset += add.length-2;
157 } else {
158 return;
159 }
160 e.preventDefault();
161 if (!document.execCommand("insertText", false, "\n"+add)) {
162 //Firefox
163 area.selectionEnd = area.value.length;
164 area.setRangeText("\n"+add+rest);
165 area.selectionEnd = null;
166 } else {
167 area.selectionEnd = area.value.length;
168 area.setSelectionRange(area.selectionStart, area.value.length);
169 document.execCommand("insertText", false, rest);
170 area.selectionEnd = null;
171 area.focus();
172 }
173 area.selectionStart = pos+offset;
174 return;
175 if (document.execCommand("insertText", false, "\n"+add)) {
176 //area.setSelectionRange(area.selectionStart, text.length);
177 // alert("rest=["+rest+"], add=["+add+"]");
178 alert(text.substring(pos, area.value.length));
179 if (rest != rest0) {
180 area.setSelectionRange(pos, area.value.length);
181 return;
182 document.execCommand("delete");
183 }
184 document.execCommand("insertText", false, rest);
185 } else {
186 // Firefox cannot use insertText in textarea...
187 area.value = text.substring(0, pos) + "\n" + add + rest;
188 }
189 //area.setSelectionRange(pos+length(add));
190 area.selectionStart=area.selectionEnd = (pos + offset);
192 }
193 }
194 function helpMarkdown(e) {
195 switch (e.keyCode) {
196 case 8: helpMarkdownBS(e); break;
197 case 13: helpMarkdownEnter(e); break;
198 }
199 }
200 /* Init event listeners */
201 function addFileInput() {
202 var inpfile = collectElementsByAttr("input", "name", "image");
203 if (!inpfile) return;
204 var filled = true;
205 var i, ih;
206 for (i of inpfile) {
207 if (! i.value) filled=false;
208 }
209 if (filled) {
210 ih = i.parentNode.innerHTML;
211 if (ih) {
212 var inpf = ih.substring(ih.indexOf("<input")),
213 newi = "<br>"+inpf.substring(0, inpf.indexOf(">")+1);
214 i.insertAdjacentHTML("afterend", newi)
215 // alert(newi);
216 }
217 }
218 }
219 function initFileInput() { // Multiplies "input type=file"
220 var el, morefile = document.getElementById("morefile");
221 if (morefile) {
222 for (el of collectElementsByAttr("input", "name", "image")) {
223 el.addEventListener("change", function(ev) {
224 if (ev.target.value > "" && ev.target.files.length == 1)
225 morefile.style.visibility = "visible";
226 // No need to hide again, sure?
227 });
228 }
229 morefile.addEventListener("click", addFileInput, null);
230 }
231 // When renaming, select basename part
232 for (el of collectElementsByAttr("input", "class", "mv")) {
233 el.addEventListener("focus", function(ev) {
234 var i = ev.target;
235 if (i) {
236 i.setSelectionRange(0, i.value.lastIndexOf("."));
237 }
238 });
239 }
240 }
241 function initTextarea() {
242 var te = collectElementsByAttr("textarea", "name", "text");
243 if (!te || !te[0]) return;
244 te[0].addEventListener("keydown", helpMarkdown, false);
245 }
246 function initBlogs() {
247 // Auto-complete #xxxx
248 var check = collectElementsByAttr("input", "name", "notifyto");
249 if (check)
250 for (let i of check) {
251 i.addEventListener("click", insertRedirect, null);
252 }
253 for (let i of document.getElementsByTagName("a"))
254 if (i.getAttribute("href").match(/^#[0-9]+$/))
255 if (RegExp.lastMatch == i.innerHTML)
256 i.addEventListener("click", insertRedirect, null)
257 }
258 function initGrpAction() {
259 var rev = document.getElementById("reverse");
260 if (!rev) return; // Is not grpAction page
261 if (rev.tagName.match(/span/i)) {
262 rev.textContent = " 反転 ";
263 rev.addEventListener("click", reverseChecks, null);
264 }
265 var emailbtn = document.getElementById("email");
266 emailbtn.addEventListener("click", function(ev){
267 // Enlarge box and Select user's checkbox
268 if (!ev.target.checked) return;
269 var x = collectElementsByAttr("div", "class", "foldtabs");
270 if (x && x[0] && x[0].style) {
271 x[0].style.height = "10em";
272 }
273 let myuid = document.getElementById("myuid");
274 if (myuid) {
275 let usel = collectElementsByAttr("input", "name", "usel");
276 if (usel) {
277 for (u of usel) {
278 if (u.value == myuid.value)
279 u.checked = true;
280 }
281 }
282 }
283 }, null);
284 var teamsel = document.getElementById("selteam");
285 if (teamsel) {
286 var usel, p, team;
287 // Select all members of the team
288 teamsel.addEventListener("change", function(ev) {
289 var teamname = teamsel.value,
290 selected = new RegExp('(^| )'+teamname+"($|,)");
291 usel = collectElementsByAttr("input", "name", "usel");
292 if (!usel) return;
293 for (u of usel) {
294 p = u.parentNode; // should be label
295 if (!p) continue;
296 if (teamname == "TEAM") { // Reset all checks
297 u.checked = false; // when "TEAM" is selected
298 } else {
299 p = p.parentNode.parentNode;// should be tr
300 team = nthChildOf(p, 3, "td")
301 if (team && team.textContent
302 && team.textContent.match(selected)) {
303 u.checked = true;
304 }
305 }
306 }
307 }, null);
308 }
309 }
310 function initGrphome() {
311 console.log("initGrphome");
312 let btn = document.querySelectorAll("button.toggle-frozen");
313 if (!btn) return;
314 let url = document.URL,
315 mypath = url.substring(url.lastIndexOf("/"));
316 if (mypath.match(/(.*)\/(.*)/)) {
317 mypath = RegExp.$2;
318 mypath = mypath.substring(0, mypath.lastIndexOf("?"));
319 //alert("mypath="+mypath);
320 } else return;
322 function toggleFrozen(e, rowid) {
323 let tgt = mypath+"?blog_setfrozen+"+rowid;
324 let tr = e.target.parentNode.parentNode;
325 fetch(tgt, {
326 method: "POST",
327 headers: {'Content-Type': 'text/html; charset=utf-8'},
328 }).then(function(resp) {
329 return resp.text();
330 }).then(function(tbody) {
331 try {
332 var json = JSON.parse(tbody);
333 } catch (e) {
334 return;
335 }
336 let state = json.state, newstate;
337 if (json.alert) {
338 alert(json.alert)
339 }
340 if (state.match(/frozen/i)) {
341 newstate = "凍結";
342 } else {
343 newstate = null;
344 }
345 tr.setAttribute("class", newstate);
346 });
347 }
348 for (let b of btn) {
349 let rowid = null;
350 let td=b.parentNode, tr = td.parentNode;
351 for (let a of tr.querySelectorAll("a[href]")) {
352 if (a.getAttribute("href").match(/\?replyblog\+([0-9]+)/)) {
353 rowid = parseInt(RegExp.$1);
354 break;
355 }
356 }
357 if (rowid && rowid>0) {
358 b.addEventListener("click", function(e) {
359 if (!btn) return;
360 toggleFrozen(e, rowid);
361 }, false);
362 b.setAttribute("title", "稼動/凍結をその場で切り替えます\n\
363 Toggle Running/Frozen ("+rowid+")");
364 }
365 }
366 }
367 function init() {
368 initGrpAction();
369 initBlogs();
370 initFileInput();
371 initTextarea();
372 initGrphome();
373 }
374 document.addEventListener('DOMContentLoaded', init, null);
375 })();