メタデータ-JPビューワー ソース
アトリビューション・メタデータ-JP » メタデータ-JPビューワー ソース
<head> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0"/> <script src="2" defer></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> <link rel="stylesheet" href="3"> </head> <body> <table id="main"> <thead></thead> <tbody></tbody> </table> </body>
var __SITE_ID__ = 578002; // scp-jp var __BASE_URL__ = 'http://scp-jp.wikidot.com'; var STRING = {}; STRING.UTYPE = { AUTHOR : '著者', REWRITE : '改稿', TRANSFERER: '移転' }; STRING.ERROR = { NON_CHROMIUM_MESSAGE : 'エラー位置を取得できませんでした。エラー位置を表示する場合は、PC版Google Chrome / Edge(最新版)で確認してください。', INVALID_SRC_FORMAT_1 : 'ソースのフォーマットが不正です。', INVALID_SRC_FORMAT_2 : '「ソース」タブ内のリストを修正してください。', INVALID_SRC_FORMAT_NOTE: '変更の反映には時間がかかる場合があります。', _POSITION : 'エラー位置' }; STRING.TABLE_HEADER = { TITLE: 'タイトル', TYPE : '種別', UNAME: 'ユーザー名', DATE : '日付' }; STRING.LOADING = { TITLE : 'ページタイトルを読み込み中', ACCOUNT : 'アカウント情報を読み込み中', COMPLETE: '帰属情報を読み込みました。' }; STRING.FN = { DAYS_AGO: DAYS => `${DAYS}日前`, COUNTS : { ARTICLES: NUM => `${NUM} 記事`, ITEMS : NUM => `${NUM} 件` } }; // main var param = location.search.slice(1).split('&') .filter(v => v) .reduce((p, v) => { var _ = v.match(/(.+?)=(.*)/); p[_[1]] = _[2]; return p; }, {}); var sleep = ms => new Promise(r => setTimeout(r, ms)); var _FetchStock = []; var Fetch = class { constructor(url, option={}) { this.fetch = Fetch._retry(url, option); _FetchStock.push(this); } async request() { return (await this.fetch).clone(); } async getBytes() { var buf = await this.arrayBuffer(); var u8a = new Uint8Array(buf); return u8a.length; } static async _retry(url, option, interval = 250) { try { return await fetch(url); }catch(e) { await sleep(interval); return await Fetch._retry(url, interval); } } static async getAllBytes() { return (await Promise.all( _FetchStock.map(f => f.getBytes()) )) .reduce((a, b) => a + b); } }; ['text', 'json', 'blob', 'arrayBuffer', 'formData'] .forEach(m => { Fetch.prototype[m] = async function() { return await (await this.request())[m](); }; }); var getJSON = async url => { var f = new Fetch(url), json = null, error = null; try { json = await f.json(); }catch(e) { console.log('Caught Error:', e); var pos = (e.message.match(/JSON at position (\d+)/) || [])[1]; error = `${e.message}<hr /><b>[ ! ]:</b> ${STRING.ERROR.NON_CHROMIUM_MESSAGE}`; if(!isNaN(pos)) { pos = Number(pos); var text = await f.text(); var rel_bef = text.slice(0, pos).split('\n').slice(-4).length > 3 ? '<span class="ignore">...\n</span>' : ''; var before = rel_bef + text.slice(0, pos).split('\n').slice(-3).join('\n').replace(/(\s*?[\s\S]\s*?)$/, `<span class="error-inline tgt">$1</span>`); var rel_aft = text.slice(pos).split('\n').slice(0, 4).length > 3 ? '<span class="ignore">\n...</span>' : ''; var after = text.slice(pos).split('\n').slice(0, 3).join('\n') + rel_aft; var option = `{behavior:'smooth', block:'center', inline:'center'}`; error = `${e.message}\n<b>${STRING.ERROR._POSITION}:</b> <span class="error-inline" onclick="document.querySelector('.error-inline.tgt').scrollIntoView(${option})">${pos}</span><hr />${before}${after}`; } } return {json: json, error: error}; } var Karma = async id => { var kar = new Fetch(`/userkarma.php?u=${id}`); var buf = await kar.arrayBuffer(); var u8a = new Uint8Array(buf); if(u8a[0] != 137) throw new Error('Invalid ID'); var lv, pro = false; switch(u8a.length) { case 295:lv=0; break; case 304:lv=1; break; case 306:lv=2; break; case 310:lv=3; break; case 311:lv=4; break; case 309:lv=5; break; case 1094:lv=0; pro=true; break; case 1099:lv=1; pro=true; break; //case XXX:lv=2; pro=true; break; case 1098:lv=3; pro=true; break; case 1097:lv=4; pro=true; break; case 1093:lv=5; pro=true; break; //default:break; default:lv=2; pro=true; break; } return {lv:lv, pro:pro}; } var _isMemberCache = {}; var isMember = async username => { var un = username.trim(); var cache = _isMemberCache[un]; if(cache) return cache; var url = `/quickmodule.php?module=MemberLookupQModule&q=${un}&s=${__SITE_ID__}`; var users = (await getJSON(url)).json.users || []; var res = users.find(u => u.name.toLowerCase().trim() == un.toLowerCase()) || false; if(res) { res.name = res.name.trim(); var k = await Karma(res.user_id); res.karma = k.lv; res.pro = k.pro; } _isMemberCache[un] = res; return res; }; // dom var start = new Date; var table = document.querySelector('#main'); var thead = table.querySelector('thead'); var tbody = table.querySelector('tbody'); var toTR = (child_type, ...txts) => { var tr = document.createElement('tr'); tr.__child__ = []; txts.forEach(t => { var child = document.createElement(child_type); child.innerHTML = t; tr.appendChild(child); tr.__child__.push(child); }); return tr; }; var makeHeader = (...txts) => { var tr = toTR('th', ...txts); thead.appendChild(tr); return tr; }; var makeItem = (...txts) => { var tr = toTR('td', ...txts); tbody.appendChild(tr); return tr; }; // main (async () => { var isGetMem = !param.simple || param.simple == 'false'; var isDebugMode = param.debug && param.debug != 'false'; var header = [ STRING.TABLE_HEADER.TITLE, STRING.TABLE_HEADER.TYPE, STRING.TABLE_HEADER.UNAME, STRING.TABLE_HEADER.DATE ]; makeHeader(...header); var prog_parent = makeItem(''); var prog = prog_parent.__child__[0]; prog.colSpan = header.length; prog.classList.add('progress'); var req = await getJSON( `../attribution-metadata-jp/1` ); if(req.error) { prog.innerHTML = `<div>${STRING.ERROR.INVALID_SRC_FORMAT_1}</div><div>${STRING.ERROR.INVALID_SRC_FORMAT_2}</div><div class="note">${STRING.ERROR.INVALID_SRC_FORMAT_NOTE}</div><pre>${req.error}</pre>`; prog.classList.add('error'); return; } var list = req.json .sort((a, b) => a.pagename > b.pagename ? 1 : -1); var currentTitle, isNew, tgt, odd = false; for(var item of list) { isNew = false; var title = item.pagename; if(currentTitle != title) { odd = !odd; currentTitle = title; var title_esc = title.replace(/[&'`"<>]/g, v => { return {'&': '&', "'": ''', '`': '`', '"': '"', '<': '<', '>': '>'}[v]; }); var a = `<a class="title" target="_blank" href="${__BASE_URL__}/${item.pagename}" title="[ ! ] ${title_esc}">${title}</a>`; isNew = true; } var utype = { author: `<span class="utype author">${STRING.UTYPE.AUTHOR}</span>`, rewrite: `<span class="utype rewrite">${STRING.UTYPE.REWRITE}</span>`, transferer: `<span class="utype transferer">${STRING.UTYPE.TRANSFERER}</span>` }[item.type]; var n = item.username; var mspan = `<a target="_blank" class="user" data-ismember="false" data-karma="" data-ispro="false" title="[ ! ] ${n}">${n}</a>`; var date = item.date ? `<div title="${STRING.FN.DAYS_AGO(Math.floor((new Date - new Date(item.date)) / 1000 / 60 / 60 / 24))}">${item.date}</div>` : ''; var item = makeItem(...(isNew ? [a] : []), ...[utype, mspan, date]); if(isNew) { tgt = item.__child__[0]; tgt.rowSpan = 1; } else { tgt.rowSpan++; } item.classList.add(odd ? 'odd' : 'even'); item.__child__[isNew ? 2 : 1].classList.add('user_wrap'); } // get info var links = [...document.querySelectorAll('a.title')] .reduce((p, v) => (p[v.innerText.trim()] = v, p), {}); var title_dict = {}, i = 1; var articles = list.map(l => l.pagename).filter((v,i,a) => a.indexOf(v) == i); var title_tgts = articles.map(v => v.slice(0, 5)).filter((v,i,a) => a.indexOf(v) == i); var len = title_tgts.length; var task_1_prog = document.createElement('div'); prog.appendChild(task_1_prog); var update_t1 = i => { task_1_prog.innerHTML = `${STRING.LOADING.TITLE}...<pre>${i} / ${Object.keys(links).length}</pre>`; }; update_t1(0); var _title_i = 1; var task_1 = title_tgts.map(async tgt => { var url = `/quickmodule.php?module=PageLookupQModule&q=${tgt}&s=${__SITE_ID__}`; var pages = (await getJSON(url)).json.pages || []; for(var p of pages) { if(links[p.unix_name]) { links[p.unix_name].title = p.title; links[p.unix_name].innerText = p.title; links[p.unix_name].classList.add('loaded'); update_t1(_title_i++); } } return 1; }); if(isGetMem) { var users = [...document.querySelectorAll('a.user')] .reduce((p, v) => { var n = v.innerText.toLowerCase().trim(); p[n] ? p[n].push(v) : (p[n] = [v]); return p; }, {}); var mem_dict = {}, mem_promise; var mem_tgts = list .map(l => l.username.toLowerCase()) .filter((v,i,a) => a.indexOf(v) == i); var task_2_prog = document.createElement('div'); prog.appendChild(task_2_prog); var update_t2 = i => { task_2_prog.innerHTML = `${STRING.LOADING.ACCOUNT}...<pre>${i} / ${mem_tgts.length}</pre>`; }; update_t2(0); var _mem_i = 1; var task_2 = mem_tgts.map(async n => { var m = await isMember(n); var links = users[n]; update_t2(_mem_i++); users[n].forEach(a => { if(m) { a.innerText = m.name; a.title = m.name; a.dataset.ismember = true; a.dataset.karma = m.karma; a.dataset.ispro = m && m.pro; a.href = `http://www.wikidot.com/user:info/${n.replace(/ /g, '-')}`; } a.classList.add('loaded'); }); }); await Promise.all([...task_1, ...task_2]); } else { await Promise.all(task_1); } window.current_size = await Fetch.getAllBytes(); prog.innerHTML = `${STRING.LOADING.COMPLETE}<pre>${STRING.FN.COUNTS.ARTICLES(articles.length)} : ${STRING.FN.COUNTS.ITEMS(list.length)}</pre>`; var bytes = await Fetch.getAllBytes(); var time = new Date - start; console.log(`Transferred: ${bytes} bytes, Timing: ${time} ms`); })();
::-webkit-scrollbar { width: 11px; height: 11px; } ::-webkit-scrollbar-thumb { background: rgba(50, 50, 50, 0.6); } ::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.05); } *:hover::-webkit-scrollbar-thumb { background: rgba(50, 50, 50, 0.75); border-radius: 3px; } body { color: #333; font-family: sans-serif; font-size: .6em; margin: 0; } a { color: #b01; text-decoration: none; } a, .user { display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 12em; } a.user { width: 10em; } a:not(.user):hover { text-decoration: underline; } table { border-spacing: 3px; font-size: 1em; width: 100%; } td { background: #fff; box-shadow: 1px 1px 3px #aaa; padding: 0; position: relative; text-align: center; } td.user_wrap { padding: 0; width: 10em; } td.progress { background: #feb; font-size: 1.4em; } td.progress pre { background: #fff; box-shadow: 1px 1px 3px #aaa; display: block; font-family: Courier, monospace; margin: .25em .5em; padding: .25em 0; } td.progress.error > pre { font-size: .75em; max-width: calc(100vw - 2em - 7px); overflow-x: scroll; padding: 1em 0.5em; text-align: left; } tr.odd > td { background: #fff8f0; } .user { background: #000; box-shadow: 1px 1px 3px #aaa; cursor: pointer; display: block; font-weight: bold; height: 100%; padding: 0.25em 0.5em; text-shadow: 0 0 1px #fff, 0 0 2px #fff, 0 0 2px #fff, 0 0 2px #fff, 0 0 3px #fff; } .user[data-karma="5"] { background: linear-gradient(90deg, #61F3A7, #61F328, #FFF20E, #EC810E, #FF2D2D); } .user[data-karma="4"] { background: linear-gradient(90deg, #61F3A7, #61F328, #FFF20E, #EC810E, #000); } .user[data-karma="3"] { background: linear-gradient(90deg, #61F3A7, #61F328, #FFF20E, #000, #000); } .user[data-karma="2"] { background: linear-gradient(90deg, #61F3A7, #61F328, #000, #000, #000); } .user[data-karma="1"] { background: linear-gradient(90deg, #61F3A7, #000, #000, #000, #000); } .user[data-ismember="false"] { background: #601; color: #fff; cursor: auto; text-shadow: none; } .user[data-ispro="true"]::before { content: '\f005'; color: #fad880; display: inline; font-family: 'FontAwesome'; font-weight: 400; } .user[data-ispro="true"]::before { content: '\f005'; display: inline-block; font-family: FontAwesome; font-weight: 400; padding: 0 0.5em 0 0; text-align: center; text-rendering: auto; width: 1em; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; transform: translate(0, 0); } a.title:not(.loaded)::before { content: '[ ! ] '; color: #333; font-weight: bold; } a.user:not(.loaded) { background: #ddd; color: #333; cursor: auto; } span.utype { display: block; } span.utype.author { color: #34b; font-weight: bold; } span.utype.rewrite { color: #6b2; font-weight: bold; } span.utype.transferer { color: #982; } .note { font-size: .8em; font-style: italic; text-align: right; } .note::before { content: '※ '; } .error-inline { background: #b01; color: #fff; cursor: pointer; font-weight: bold; position: relative; } .ignore { user-select: none; } /* /code/4 */ td > input, td > textarea, select { border: 1px solid #aaa; border-radius: 0; display: block; margin: 0.25em 0.5em; transition: all .25s; width: calc(100% - 1em); } td > input:focus, td > textarea:focus, select:focus { border: 1px solid #80acf8; outline: none; } td > input#date:invalid, td > input#date.invalid { background: #fff0f0; color: #b01; } span.error-popup { background: #fff; border: 1px solid #901; color: #901; display: none; padding: 0.25em 0.5em; position: absolute; right: 0; user-select: none; z-index: 2; } td > input#date:invalid + span.error-popup, td > input#date.invalid + span.error-popup { display: inline-block; } td > textarea { font-family: Courier, monospace; height: 3em; }
<head> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0"/> <link rel="stylesheet" href="3"> </head> <body> <table id="main"> <thead> <tr> <th>ページ名</th> <th>ユーザー名</th> <th>タイプ</th> <th>日付</th> </tr> </thead> <tbody> <tr class="odd"> <td> <input type="text" id="title" placeholder="attribution-metadata-jp"/> </td> <td> <input type="text" id="user" placeholder="7happy7"/> </td> <td> <select id="type"> <option value="author">原作者・共著者</option> <option value="rewrite">改稿</option> <option value="transferer">移転</option> </select> </td> <td> <input type="text" id="date" pattern="^(20\d{2}-[0-1]\d-\d{2}|)$" placeholder="2022-01-01"/> <span class="error-popup">無効な日付です</span> </td> </tr> <tr class="odd"> <td colspan="4"> <div>コピペ用:</div> <textarea id="area" readonly onfocus="this.select()" onblur="window.getSelection().removeAllRanges()"></textarea> </td> </tr> </tbody> </table> <script> var [_title, _type, _user, _date, _area] = ['title', 'type', 'user', 'date', 'area'] .map(id => document.querySelector(`#${id}`)); var checkInvalidDate = () => { var _ = _date.value != '' && isNaN((new Date(_date.value)).getTime()); if(_) _date.classList.add('invalid') else _date.classList.remove('invalid'); return _; }; var callback = e => { var obj = {}; _area.value = JSON.stringify(obj); _area.value = `{"pagename": "${ _title.value }", "username": "${ _user.value }", "type": "${ _type.value }", "date": "${ checkInvalidDate() || _date.validity.patternMismatch ? '' : _date.value }"}`; }; _date.addEventListener('keyup', checkInvalidDate); [_title, _type, _user, _date] .forEach(i => i.addEventListener('change', callback)); callback(); </script> </body>