s4
changeset 898:411ce55c0dae
AJAX posting and PJAX file-viewer initially introduced.
author | HIROSE Yuuji <yuuji@gentei.org> |
---|---|
date | Sat, 02 Jan 2021 15:15:03 +0900 |
parents | 3d437261edca |
children | a4ad4101064d |
files | examples/common/default/default.css s4-blog.sh s4-main.js scripts/s4-sns.case |
diffstat | 4 files changed, 449 insertions(+), 42 deletions(-) [+] |
line diff
1.1 --- a/examples/common/default/default.css Thu Dec 31 10:31:13 2020 +0900 1.2 +++ b/examples/common/default/default.css Sat Jan 02 15:15:03 2021 +0900 1.3 @@ -4,6 +4,9 @@ 1.4 body {background: #eff; margin: 2px; padding: 6px;} 1.5 h2, h3, hr {clear: both;} 1.6 *.warn {color: red;} 1.7 +*.warnbg {background: red;} 1.8 +*.hidden {visibility: hidden;} 1.9 +*.border {border: 1px solid #113;} 1.10 div.topmenu { 1.11 margin: 0; padding: 0; width: 100%; height: 2em; 1.12 } 1.13 @@ -35,7 +38,9 @@ 1.14 table.td2r td:nth-child(2) {text-align: right;} 1.15 table.td3r td:nth-child(3) {text-align: right;} 1.16 table.td3rr td:nth-child(n+3) {text-align: right;} 1.17 -table.td3evw th:nth-child(2n+4) {background: white;} 1.18 +table.td3evw th:nth-child(2n+4), span.textdigest { 1.19 + background: white; 1.20 +} 1.21 table.thl th {text-align: left;} 1.22 span#reverse {background: white; padding: 0 0 0 0.4ex; border: outset;} 1.23 1.24 @@ -165,6 +170,24 @@ 1.25 table.mini th, table.mini td {text-align: justify;} 1.26 table.mini td, table.mini th {padding: 1px 0.5ex; min-width: 1em;} 1.27 table.mini {margin-left: 1em; border-left: 2px solid #686;} 1.28 +div.pjaxview, div.pjaxview2 { 1.29 + position: fixed; top: 1ex; left: 0; width: 9vw; height: 9vh; 1.30 + background: #ffffee; border: 1px double navy; border-radius: 2em; 1.31 + z-index: 7; 1.32 +} 1.33 +div.pjaxview2 { 1.34 + width: 100vw; height: 95vw; transition: 0.5s; 1.35 +} 1.36 +div.pjaxview iframe { 1.37 + width: 98%; height: 90%; object-fit: scale-down; 1.38 +} 1.39 +div.pjaxview p { 1.40 + padding: 0.5ex; text-align: left; margin: 0 2em; 1.41 + white-space: nowrap; overflow: hidden; 1.42 +} 1.43 +div.pjaxview p button { font-size: large; padding: 0 1em;} 1.44 +div.pjaxview p button { background: #eeeee0; } 1.45 +div.pjaxview p button:focus { background: #fffff8; } 1.46 1.47 /* "visibility: collapse" not working on chromium browser */ 1.48 div.noprofimg tr.profimg {visibility: collapse; display: none;} 1.49 @@ -393,9 +416,13 @@ 1.50 border-radius: 1em; border: double 5px blue; 1.51 min-width: 10em; right: 0; bottom: 0; 1.52 } 1.53 -.dissolving { 1.54 - opacity: 0; transition: 3.0s; 1.55 +.dissolving {opacity: 0; transition: 3.0s;} 1.56 +.emerging {opacity: 1; transition: 3s;} 1.57 +span.loading, input#c:disabled { 1.58 + visibility: visible; transform: rotateX(3600deg); transition: 30s; 1.59 + display: inline-block; 1.60 } 1.61 +span.loading {padding: 0 1em;} 1.62 1.63 /* 1.64 * PR Web
2.1 --- a/s4-blog.sh Thu Dec 31 10:31:13 2020 +0900 2.2 +++ b/s4-blog.sh Sat Jan 02 15:15:03 2021 +0900 2.3 @@ -178,6 +178,7 @@ 2.4 fi 2.5 fi 2.6 fi 2.7 + err "blog_showentry Entered: `gdate +%S.%03N` blogrowid=$rowid" 2.8 blog_notify=`getvalbyid blog notify "$rowid"` 2.9 blog_team=`blog_getteam "$rowid"` 2.10 blog_mode=`getvalbyid blog mode "$rowid"` 2.11 @@ -201,6 +202,12 @@ 2.12 THEN '' 2.13 ELSE 'Unreadable' 2.14 END" 2.15 + elif [ x"$blog_mode" = x"report-closed" ]; then 2.16 + F_UNREADABLE="CASE 2.17 + WHEN author = '$user' 2.18 + THEN '' 2.19 + ELSE 'Unreadable' 2.20 + END" 2.21 else 2.22 F_UNREADABLE="'Unreadable'" 2.23 fi 2.24 @@ -226,6 +233,21 @@ 2.25 #err id=$id 2.26 #err "select val from $ts where key='title' and id='$id';" 2.27 2.28 + ## Parse control sequence 2.29 + nlimit=$listartlimit 2.30 + case "$control" in 2.31 + n:[Aa][Ll][Ll]) 2.32 + unset nlimit ;; 2.33 + n:*) 2.34 + nlimit=${control##*:} 2.35 + nlimit=${nlimit%%[!A-Z0-9a-z_]*} 2.36 + ;; 2.37 + f:[Aa][Ll][Ll]) ;; 2.38 + f:2???-??-??*) # f:2020-12-27T08:02:43 2.39 + fetch=${control#f:} 2.40 + fetch_ajax=`echo "'$fetch'"|tr T ' '` 2.41 + esac 2.42 + err control=$control fetch_ajax=$fetch_ajax 2.43 #(1)Display root article 2.44 cat<<EOF 2.45 <form class="replyblog" action="$myname?replyblog+${rowid}#bottom" method="POST" enctype="multipart/form-data"> 2.46 @@ -271,18 +293,21 @@ 2.47 EOF 2.48 if test -s $midfile && IFS='|' read edit ctime hexhead blogtype < $midfile 2.49 then 2.50 - cat<<-EOF 2.51 + if [ -z "$fetch_ajax" ]; then # UUUUU 2.52 + 2.53 + cat<<-EOF 2.54 <tr><td>${edit:+$href }$ctime $blogtype $href2${edit:+$href3} $href4 $href5</td></tr> 2.55 <tr class="preface${frozen_class:+ }$frozen_class"> 2.56 <td>`echo "$hexhead"|unhexize|htmlescape|hreflink|minitbl`</td></tr> 2.57 </table> 2.58 EOF 2.59 - case "$blogtype" in 2.60 - "クイズ"|"XXXX集計") 2.61 - echo "${blogtype}モードは本人と管理者の書き込みのみが表示されます。" 2.62 - ;; 2.63 - esac | html p 'class="warn"' 2.64 + case "$blogtype" in 2.65 + "クイズ"|"XXXX集計") 2.66 + echo "${blogtype}モードは本人と管理者の書き込みのみが表示されます。" 2.67 + ;; 2.68 + esac | html p 'class="warn"' 2.69 2.70 + fi # UUUUU 2.71 if [ x"$blogtype" = x"クイズ" -o x"$blogtype" = x"XXXX集計" ]; then 2.72 if $isgroup; then 2.73 # Failsafe to query timeout 2.74 @@ -307,16 +332,7 @@ 2.75 echo "時間をおいて繋いでください(Please visit later)." | html p 2.76 return 2.77 fi 2.78 - ## Parse control sequence 2.79 - nlimit=$listartlimit 2.80 - case "$control" in 2.81 - n:[Aa][Ll][Ll]) 2.82 - unset nlimit ;; 2.83 - n:*) 2.84 - nlimit=${control##*:} 2.85 - nlimit=${nlimit%%[!A-Z0-9a-z_]*} 2.86 - ;; 2.87 - esac 2.88 + 2.89 lkhome="<a href=\"$myname?home" lke='">' 2.90 lkedit="<a href=\"$myname?editart" 2.91 hlink="$myname?home" elink="$myname?editart" 2.92 @@ -392,7 +408,8 @@ 2.93 $cond_qz) a 2.94 LEFT JOIN 2.95 a_s s 2.96 - ON a.id=s.id; 2.97 + ON a.id=s.id 2.98 + ${fetch_ajax:+WHERE s.TIME > $fetch_ajax}; 2.99 EOF 2.100 if [ $? -ne 0 -a ! -s $midfile ]; then 2.101 echo "時間をおいてください(Visit later please)." | html p 2.102 @@ -426,6 +443,7 @@ 2.103 else 2.104 CAT=cat 2.105 fi 2.106 + err "blog_showentry Started: `gdate +%S.%03N` ${fetch_ajax:+ajax}" 2.107 # Start blog_replies table 2.108 $CAT $midfile | 2.109 while IFS='|' read id edit notify uid author uname icon aid \ 2.110 @@ -487,6 +505,7 @@ 2.111 usecache='' tsfile=$td/$id.stamp 2.112 for i in $imgids; do 2.113 mrid=${i%%:*}; i=${i#*:}; sz=`size_h ${i%%:*}` 2.114 + _href="href=\"$catlink+$mrid\"" 2.115 fn=`echo "${i#*:}"|unhexize` 2.116 fnb=$fn"(${sz})" 2.117 case "$fn" in 2.118 @@ -508,7 +527,7 @@ 2.119 };}; then 2.120 usecache=1 # Set usecache flag on 2.121 cat<<-EOF 2.122 - <a href="$catlink+$mrid"><img src="$outfile"> 2.123 + <__UNCLICKABLE__><a $_href><img src="$outfile"> 2.124 $fnb</a> 2.125 EOF 2.126 # !!NOTE!! Create row stamp ONLY WHEN imgcache is active 2.127 @@ -522,18 +541,18 @@ 2.128 cat "$outfile" \ 2.129 | hexize \ 2.130 | sed -e 's/\(..\)/%\1/g' \ 2.131 - -e "s|^|<a href=\"$catlink+$mrid\"><img src=\"data:image/$fmt,|" \ 2.132 + -e "s|^|<__UNCLICKABLE__><a $_href><img src=\"data:image/$fmt,|" \ 2.133 -e "s|\$|\">$fnb</a>|" 2.134 unset stampfile # img data stream is not suitable to cache 2.135 echo $tm > $tsfile 2.136 else # Failed to convert 2.137 rm -f $outfile 2.138 - echo "<a href=\"$catlink+$mrid\">$fnb</a>" 2.139 + echo "<__UNCLICKABLE__><a $_href>$fnb</a>" 2.140 fi 2.141 fi 2.142 ;; 2.143 *) 2.144 - echo "<__UNREADABLE__><a href=\"$catlink+$mrid\"><img src=\"$deficon\">$fnb</a>" 2.145 + echo "<__UNCLICKABLE__><a $_href><img src=\"$deficon\">$fnb</a>" 2.146 ;; 2.147 esac 2.148 done 2.149 @@ -548,10 +567,18 @@ 2.150 fi 2.151 test -n "$stampfile" && date "+%F %T" > $stampfile 2.152 fi 2.153 + if [ -n "$fa" ]; then 2.154 + replhref="s/a href=[^>]*>/a>/" 2.155 + else 2.156 + replhref="" 2.157 + fi 2.158 # Printing a cached row 2.159 sed -e "/^<td class=/s/__NEWCLS__/$new${new:+ }/" \ 2.160 -e "/^<td class=/s,__EDIT__,$editlink," \ 2.161 -e "/^<__NOTIFY__>/s,,${notify:+$nt}," \ 2.162 + ${replhref:+-e "/^<__UNCLICKABLE__>/$replhref"} \ 2.163 + ${replhref:+-e "/^<__UNREADABLE__>/$replhref"} \ 2.164 + -e "/<__UNCLICKABLE__>/s///" \ 2.165 -e "/<__UNREADABLE__>/s,,${fa:+$cannotread}," \ 2.166 $cachefile 2.167 done 2.168 @@ -589,10 +616,12 @@ 2.169 `cgi_file image "" "$file_accept title=\"$filehelp\" multiple"` 2.170 </td></tr> 2.171 </table> 2.172 -<input type="submit" value="送信" class="$blog_notify" title="$ntmode"> 2.173 +<input type="hidden" name="fetchtime" value="`date +%FT%T`"> 2.174 +<input type="hidden" name="filesize_max" value="$filesize_max"> 2.175 +<input type="submit" id="c" value="送信" class="$blog_notify" title="$ntmode"> 2.176 <input type="reset" value="リセット"></div></div> 2.177 EOF 2.178 - ) 2.179 + ) 2.180 cat<<-EOF 2.181 </table> <!-- end of s4-blog:blog_showentry() main table --> 2.182 <p class="update_link"><a 2.183 @@ -615,6 +644,7 @@ 2.184 [ -s $iconcleaner ] && query ".read '$iconcleaner'" 2.185 # Record access log 2.186 acclog blog $rowid 2.187 + err "blog_showentry Finished: `gdate +%S.%03N` ${fetch_ajax:+ajax}" 2.188 } 2.189 2.190 lshandout() { 2.191 @@ -1522,6 +1552,13 @@ 2.192 err "blog_reply Finished: `gdate +%S.%03N` user=$user owner=[$owner] title=[$title]" 2.193 } 2.194 2.195 +blog_fetch() { 2.196 + contenttype "text/plain; charset=utf-8"; echo 2.197 + err blog_fetch: blog "$@" 2.198 + blog_reply "$@" 2.199 + # blog_showentry blog "$@" 2.200 +} 2.201 + 2.202 blog_reply_article() { # Direct link to article in some blog 2.203 arid=${1:-0} # Already sanitized to digits 2.204 brid=`query "SELECT rowid FROM blog WHERE \
3.1 --- a/s4-main.js Thu Dec 31 10:31:13 2020 +0900 3.2 +++ b/s4-main.js Sat Jan 02 15:15:03 2021 +0900 3.3 @@ -1,5 +1,16 @@ 3.4 // 愛 3.5 (function (){ 3.6 + var isOlderJS; // Set in init(); 3.7 + var hasTouchPad = 3.8 + (navigator.maxTouchPoints && navigator.maxTouchPoints >0); 3.9 + var myurl = document.URL, 3.10 + mypath = myurl.substring(myurl.lastIndexOf("/")); 3.11 + var art_m_list = []; 3.12 + if (mypath.match(/(.*)\/(.*)/)) { 3.13 + mypath = RegExp.$2; 3.14 + mypath = mypath.substring(0, mypath.lastIndexOf("?")); 3.15 + //alert("mypath="+mypath); 3.16 + } 3.17 function collectElementsByAttr(elm, attr, val) { 3.18 var e = document.getElementsByTagName(elm); 3.19 if (!e) return null; 3.20 @@ -67,6 +78,325 @@ 3.21 textarea.value = lines.join("\n"); 3.22 } 3.23 } 3.24 + function registPjaxViewers(aHrefList) { 3.25 + // if (isOlderJS) return; 3.26 + let apos=art_m_list.length; 3.27 + for (let a of aHrefList) { 3.28 + let href = a.getAttribute("href"); 3.29 + let localvar = apos; 3.30 + let td = a.parentNode, 3.31 + tr = td.parentNode, 3.32 + id = td.id, 3.33 + text = td.textContent, 3.34 + author = tr.getElementsByTagName("a"); 3.35 + if (author) author = author[0].getAttribute("title"); 3.36 + if (href.match(/\?showattc\+article_m\+([0-9+])/)) { 3.37 + if (td.innerHTML.match(/x読み取り不可/)) { 3.38 + a.removeAttribute("href"); 3.39 + continue; 3.40 + } 3.41 + let url = RegExp.lastMatch; 3.42 + // console.log("pjaxView(e, "+href+", "+apos+")"); 3.43 + a.addEventListener("click", function(e) { 3.44 + // Shoud use closure local variable: localvar 3.45 + pjaxView(e, href, localvar); 3.46 + }, false); 3.47 + apos++; 3.48 + art_m_list.push({ 3.49 + url: href, id: id, author: author, text: text 3.50 + }); 3.51 + } 3.52 + } 3.53 + } 3.54 + var ajaxSubmit; 3.55 + function replAddNews(newtable) { 3.56 + let newids = [], idlist=[]; 3.57 + let getArticleID = function (td) { 3.58 + return parseInt(td.parentNode.getElementsByTagName("td")[1].id); 3.59 + } 3.60 + for (let i of newtable.querySelectorAll("td.repl")) 3.61 + newids.push(i); 3.62 + newids = newids.sort((a,b)=> { 3.63 + return (getArticleID(a) - getArticleID(b)); 3.64 + }); 3.65 + for (i of newids) 3.66 + idlist.push(getArticleID(i)); 3.67 + console.log("IDList="+idlist.join()); 3.68 + let cnt=0; 3.69 + let current = collectElementsByAttr("td", "class", "repl"), 3.70 + ncur=0, n, icur=0, o, oid, nid, otr; 3.71 + current = document.querySelectorAll('td[class="repl"]'); 3.72 + let last=current[current.length-1], 3.73 + tbody = last.parentNode.parentNode; 3.74 + // Now reconstruct articles with merge-sort like method 3.75 + outer: for (; ncur<newids.length; ncur++) { 3.76 + n = newids[ncur]; 3.77 + if (!n.id) continue; 3.78 + nid = parseInt(n.id); 3.79 + if (nid<=0) continue; 3.80 + let ntr = n.parentNode; 3.81 + for (; icur<current.length; icur++) { 3.82 + o = current[icur]; 3.83 + otr = o.parentNode; 3.84 + oid = getArticleID(o); 3.85 + if (!oid || oid=="") continue; 3.86 + if (oid >= nid) { 3.87 + ntr.getElementsByTagName("td")[0].classList.add("new"); 3.88 + tbody.insertBefore(ntr, otr); 3.89 + if (oid==nid) otr.remove(); 3.90 + cnt++; 3.91 + continue outer; 3.92 + } 3.93 + } 3.94 + // Append absolutely new articles. 3.95 + ntr = n.parentNode; 3.96 + ntr.getElementsByTagName("td")[0].classList.add("new"); 3.97 + tbody.appendChild(ntr); 3.98 + registPjaxViewers(ntr.querySelectorAll("a[href]")); 3.99 + ntr.classList.add("dissolving"); 3.100 + ntr.scrollIntoView({behavior: "smooth"}); 3.101 + setTimeout(() => { 3.102 + ntr.classList.remove("dissolving"); 3.103 + ntr.classList.add("emerging"); 3.104 + }, 100); 3.105 + cnt++; 3.106 + } 3.107 + ajaxSubmit.value = ajaxSubmit.back; 3.108 + ajaxSubmit.disabled = false; 3.109 + console.log("Update "+cnt+"rows"); 3.110 + return cnt; 3.111 + } 3.112 + 3.113 + function warnFileSize(form) { 3.114 + let szmax = form.querySelector('input[name="filesize_max"]').value; 3.115 + if (!szmax || szmax=="") return; 3.116 + szmax = parseInt(szmax); 3.117 + if (szmax <= 0) return; 3.118 + // szmax = 10000 3.119 + let ng = "", rcval=false, fileexists=false; 3.120 + for (let f of form.querySelectorAll('input[type="file"]')) { 3.121 + let thiserr = false 3.122 + for (let i of f.files) { 3.123 + fileexists = true; 3.124 + let fn = i.name, sz = i.size; 3.125 + console.log("max="+szmax+", fn="+fn+", sz="+sz); 3.126 + if (sz > szmax) { 3.127 + thiserr = true; 3.128 + ng += ((ng>"" ? ", " : "")+fn) 3.129 + } 3.130 + } 3.131 + thiserr ? f.classList.add("warnbg") : f.classList.remove("warnbg"); 3.132 + } 3.133 + if (ng>"") { 3.134 + rcval = "File-size Limit Error: "+ng+"\n"+ 3.135 + "Should be less than "+szmax+"bytes.\n"+ 3.136 + szmax+"バイト未満にしてください" 3.137 + alert(rcval); 3.138 + } 3.139 + if (form.text.value == "") { 3.140 + let w; 3.141 + if (fileexists) 3.142 + w = "Fill the text area\n" + 3.143 + "添付したファイルに関する説明を入れてください。"; 3.144 + else 3.145 + w = "Enter your comment!\n何か書き込んでね!"; 3.146 + alert(w); 3.147 + rcval = (rcval || w); 3.148 + form.text.classList.add("warnbg"); 3.149 + setTimeout(() => {form.text.classList.remove("warnbg");}, 2000) 3.150 + } 3.151 + return rcval; 3.152 + } 3.153 + function ajaxPost(e) { 3.154 + e.preventDefault(); 3.155 + let rowid; 3.156 + if (!myurl.match(/replyblog\+([0-9]+)/)) return; 3.157 + rowid = RegExp.$1 3.158 + let myform = document.querySelector("form.replyblog"); 3.159 + if (warnFileSize(myform)) return; 3.160 + ajaxSubmit = e.target; 3.161 + ajaxSubmit.back = ajaxSubmit.value; 3.162 + ajaxSubmit.value = "送信中"; 3.163 + ajaxSubmit.blur(); 3.164 + ajaxSubmit.disabled = true; 3.165 + let data = new FormData(myform), 3.166 + fetchtime = data.get("fetchtime"); 3.167 + if (!fetchtime || fetchtime=="") return; 3.168 + ///*XX*/fetchtime = "2020-06-14T00:00:00";data.set("fetchtime", fetchtime) 3.169 + let act = mypath+"?blog_fetch+"+rowid+"+f:"+fetchtime; 3.170 + 3.171 + function respUpdate(tbody) { 3.172 + let div = document.createElement("div"), form, newform; 3.173 + try { 3.174 + div.innerHTML = tbody; 3.175 + form = div.querySelector("form"); 3.176 + } catch (er) { 3.177 + alert("Cannot parse fetch data"); 3.178 + return; 3.179 + } 3.180 + let update = replAddNews(form); 3.181 + let dispelem = myform.querySelector("textarea").parentNode; 3.182 + newform = new FormData(form); 3.183 + myform.reset(); 3.184 + myform.text.value = ''; 3.185 + myform.fetchtime.value = newform.get("fetchtime"); 3.186 + myform.id.value = newform.get("id"); 3.187 + if (update && update > 0) { 3.188 + let s = update + " new article" + 3.189 + (update>1 ? "s" : "") + " posted"; 3.190 + dispInfoMomentary(s, dispelem); 3.191 + } 3.192 + } 3.193 + fetch(act, { 3.194 + method: "POST", body: data, 3.195 + credentials: "include" // For older firefox 3.196 + }).then((resp) => { 3.197 + return resp.text(); 3.198 + }).then((tbody) => { 3.199 + respUpdate(tbody); 3.200 + }) 3.201 + } 3.202 + function pjaxView(ev, url, mynum) { 3.203 + ev.preventDefault(); 3.204 + let box = document.createElement("div") 3.205 + box.setAttribute("class", "pjaxview"); 3.206 + let p1 = document.createElement("p"), 3.207 + bt = document.createElement("button"), 3.208 + sl = document.createElement("button"), 3.209 + sr = document.createElement("button"), 3.210 + loading = document.createElement("span"), 3.211 + info = document.createElement("p"); 3.212 + info1 = document.createElement("span"); 3.213 + info2 = document.createElement("span"); 3.214 + iframe = document.createElement("iframe"); 3.215 + var curpos = mynum; 3.216 + var historyBase = history.length; 3.217 + 3.218 + function _setPjaxCurposInfo() { 3.219 + let len = art_m_list.length; 3.220 + let cur = art_m_list[curpos] 3.221 + info1.textContent = (1+curpos)+" of "+len+" article #"+cur.id+ 3.222 + (cur.author ? " by "+cur.author : "") + ":"; 3.223 + info2.textContent = cur.text.trim(); 3.224 + info2.setAttribute("class", "border textdigest"); 3.225 + } 3.226 + function _resetPjax() { 3.227 + // All we can do surely is to back 1 page, 3.228 + // because we cannot move to desirable entry of history list. 3.229 + history.back(); 3.230 + } 3.231 + function setSwipeAct(iframe) { 3.232 + // We cannot use DOMContentLoaded nor iframe.contentWindow here. 3.233 + // PDF.js does not construct contentWindow...? 3.234 + iframe.addEventListener("load", () => { 3.235 + loading.classList.remove("loading"); 3.236 + if (!hasTouchPad) return; 3.237 + let ifm = iframe.contentDocument; 3.238 + let startX, moveX, thresh = 100; 3.239 + ifm.addEventListener("touchstart", (e) => { 3.240 + e.preventDefault(); 3.241 + startX = e.touches[0].pageX; 3.242 + }, false); 3.243 + ifm.addEventListener("touchmove", (e) => { 3.244 + e.preventDefault(); 3.245 + moveX = e.touches[0].pageX; 3.246 + }, false); 3.247 + ifm.addEventListener("touchend", (e) => { 3.248 + if (startX < moveX && startX + thresh < moveX) { 3.249 + switchTo(e, -1); 3.250 + } else if (startX > moveX && startX - thresh > moveX) { 3.251 + switchTo(e, +1); 3.252 + } 3.253 + }, false); 3.254 + }, false); 3.255 + 3.256 + } 3.257 + function switchTo(e, direction) { 3.258 + e.preventDefault(); 3.259 + let len = art_m_list.length, cur, newpos, url; 3.260 + newpos = (curpos+len+direction)%len; 3.261 + if (curpos == newpos) return; // No need to switch to same one 3.262 + curpos = newpos; 3.263 + cur = art_m_list[curpos]; 3.264 + url = cur.url; 3.265 + // We should remove iframe once to preserve history Object 3.266 + // https://inthetechpit.com/2019/04/20/update-iframe-without-affecting-browser-history/ 3.267 + let parent = iframe.parentNode; 3.268 + // alert("D = "+direction); 3.269 + iframe.remove(); 3.270 + parent.appendChild(iframe); 3.271 + try { 3.272 + loading.classList.add("loading"); 3.273 + iframe.src = url; 3.274 + // iframe.contentDocument.location.replace(url); 3.275 + // location.replace cannot be used because PDF viewer.js 3.276 + // does not have iframe.contentDocument 3.277 + } catch (err) { 3.278 + alert("Cannot load "+src+" : "+err.name); 3.279 + } 3.280 + _setPjaxCurposInfo(); 3.281 + setSwipeAct(iframe); 3.282 + } 3.283 + function switchToByKey(e) { 3.284 + // alert("KEY="+e.key); 3.285 + switch (e.key) { 3.286 + case "ArrowLeft": 3.287 + switchTo(e, -1); break; 3.288 + case "ArrowRight": 3.289 + switchTo(e, +1); break; 3.290 + case "Escape": 3.291 + history.back(); 3.292 + } 3.293 + } 3.294 + // <div><p> 3.295 + // <button> << </button><button>Dismiss</button><button> >> </button> 3.296 + // </p><p><span> info1 </span> <span> info2 </span></p> 3.297 + // <iframe src="..."></iframe> 3.298 + // </div> 3.299 + // ==> [ << ][Dissmiss][ >> ] 3.300 + // ==> ## of ## article #xxx by AUTHOR 3.301 + sl.textContent = " << "; 3.302 + sr.textContent = " >> "; 3.303 + sl.addEventListener("click", (e) => {switchTo(e, -1);}); 3.304 + sr.addEventListener("click", (e) => {switchTo(e, +1);}); 3.305 + sl.setAttribute("title", "to="+(mynum-1)); 3.306 + sr.setAttribute("title", "to="+(mynum+1)); 3.307 + document.body.appendChild(box); 3.308 + bt.textContent = "Click to dismiss / もどる"+mynum; 3.309 + 3.310 + box.appendChild(p1); 3.311 + p1.appendChild(sl); p1.appendChild(bt); p1.appendChild(sr); 3.312 + p1.appendChild(loading); 3.313 + info.appendChild(info1); info.appendChild(info2); 3.314 + loading.textContent=" Loading..."; 3.315 + loading.classList.add("hidden"); 3.316 + loading.classList.add("loading"); 3.317 + box.appendChild(info); 3.318 + iframe.src = url; 3.319 + 3.320 + document.addEventListener("keydown", switchToByKey); 3.321 + //box.addEventListener("click", (e) => {_resetPjax();}); 3.322 + bt.addEventListener("click", (e) => {_resetPjax();}); 3.323 + // dp.addEventListener("click", (e) => {_resetPjax();}); 3.324 + info.addEventListener("click", (e) => {_resetPjax();}); 3.325 + box.appendChild(iframe); 3.326 + 3.327 + setSwipeAct(iframe); 3.328 + 3.329 + _setPjaxCurposInfo(); 3.330 + bt.focus(); 3.331 + setTimeout(() => {box.classList.add("pjaxview2");}, 10); 3.332 + // Finally update history stack 3.333 + if (history.pushState) { 3.334 + let h = location.href.replace(/#.*/, '')+"#pjaxview"; 3.335 + history.pushState({url: h}, null, h); 3.336 + window.addEventListener("popstate", (e) => { 3.337 + if (box) { 3.338 + box.remove(); box = null; 3.339 + } 3.340 + }, false); 3.341 + } 3.342 + } 3.343 function reverseChecks() { 3.344 var names = collectElementsByAttr("input", "name", "usel"); 3.345 for (let u of names) { 3.346 @@ -75,7 +405,6 @@ 3.347 } 3.348 function renumberOL(str, start) { 3.349 var stra = str.split("\n"); 3.350 - 3.351 for (var i=1; i<stra.length; i++) { 3.352 if (stra[i].match(/^[1-9][0-9]*\. /)) { 3.353 let orig=stra[i]; 3.354 @@ -192,9 +521,9 @@ 3.355 } 3.356 } 3.357 function helpMarkdown(e) { 3.358 - switch (e.keyCode) { 3.359 - case 8: helpMarkdownBS(e); break; 3.360 - case 13: helpMarkdownEnter(e); break; 3.361 + switch (e.key) { 3.362 + case "Backspace": helpMarkdownBS(e); break; 3.363 + case "Enter": helpMarkdownEnter(e); break; 3.364 } 3.365 } 3.366 /* Init event listeners */ 3.367 @@ -212,7 +541,10 @@ 3.368 var inpf = ih.substring(ih.indexOf("<input")), 3.369 newi = "<br>"+inpf.substring(0, inpf.indexOf(">")+1); 3.370 i.insertAdjacentHTML("afterend", newi) 3.371 - // alert(newi); 3.372 + i.nextSibling.nextSibling.addEventListener('change', () => { 3.373 + // next==br next.next==input[type=file] 3.374 + warnFileSize(document.forms[0]); 3.375 + }); 3.376 } 3.377 } 3.378 } 3.379 @@ -248,12 +580,28 @@ 3.380 var check = collectElementsByAttr("input", "name", "notifyto"); 3.381 if (check) 3.382 for (let i of check) { 3.383 - i.addEventListener("click", insertRedirect, null); 3.384 + i.addEventListener("click", insertRedirect, false); 3.385 } 3.386 - for (let i of document.getElementsByTagName("a")) 3.387 + for (let i of document.querySelectorAll("a[href]")) 3.388 if (i.getAttribute("href").match(/^#[0-9]+$/)) 3.389 if (RegExp.lastMatch == i.innerHTML) 3.390 - i.addEventListener("click", insertRedirect, null) 3.391 + i.addEventListener("click", insertRedirect, false) 3.392 + for (let i of document.querySelectorAll('input[value="送信"]')) { 3.393 + // let b = document.createElement("button"); 3.394 + let b = i; 3.395 + b.value = "送信!"; 3.396 + console.log("b="+b+", tc="+b.textContent); 3.397 + b.addEventListener("click", ajaxPost, false); 3.398 + // i.insertAdjacentElement('afterend', b); 3.399 + } 3.400 + for (let f of document.querySelectorAll('input[type="file"]')) { 3.401 + let form = document.forms[0]; 3.402 + f.addEventListener('change', (e) => { 3.403 + warnFileSize(form); 3.404 + }, false) 3.405 + } 3.406 + // Hack article_m links 3.407 + registPjaxViewers(document.querySelectorAll("a[href]")); 3.408 } 3.409 function initGrpAction() { 3.410 var rev = document.getElementById("reverse"); 3.411 @@ -323,13 +671,6 @@ 3.412 function initGrphome() { 3.413 console.log("initGrphome"); 3.414 // (1)Setup Frozen State Changing Button 3.415 - let url = document.URL, 3.416 - mypath = url.substring(url.lastIndexOf("/")); 3.417 - if (mypath.match(/(.*)\/(.*)/)) { 3.418 - mypath = RegExp.$2; 3.419 - mypath = mypath.substring(0, mypath.lastIndexOf("?")); 3.420 - //alert("mypath="+mypath); 3.421 - } else return; 3.422 var ja = navigator.language.match(/ja/i); 3.423 3.424 function toggleFrozen(e, rowid) { 3.425 @@ -339,6 +680,7 @@ 3.426 fetch(tgt, { 3.427 method: "POST", 3.428 headers: {'Content-Type': 'text/html; charset=utf-8'}, 3.429 + credentials: "include" 3.430 }).then(function(resp) { 3.431 return resp.text(); 3.432 }).then(function(tbody) { 3.433 @@ -420,6 +762,7 @@ 3.434 } 3.435 } 3.436 function init() { 3.437 + isOlderJS = !("insertAdjacentElement" in document.body); 3.438 initGrpAction(); 3.439 initBlogs(); 3.440 initFileInput();
4.1 --- a/scripts/s4-sns.case Thu Dec 31 10:31:13 2020 +0900 4.2 +++ b/scripts/s4-sns.case Sat Jan 02 15:15:03 2021 +0900 4.3 @@ -46,7 +46,7 @@ 4.4 echo "Refresh: 0; $newurl"; echo 4.5 exit 0 4.6 ;; 4.7 - lshandout|lshandoutall|gethandout|gethandoutcsv|gethandoutcsv2|blogseen|getteamcsv|blog_setfrozen) 4.8 + lshandout|lshandoutall|gethandout|gethandoutcsv|gethandoutcsv2|blogseen|getteamcsv|blog_setfrozen|blog_fetch) 4.9 case "$stage" in 4.10 lshandout*|blogseen*) contenttype; echo ;; 4.11 esac