Ubuntu日本語フォーラム
ログインしていません。
UJFHack-page-button : 25超投稿の1頁表示
#24 ~ #27 あたりの投稿を読むとき,特に参照しながら返信を書くとき,何度もページを移動するのが煩わしくて書いたスクリプトです.
// 投稿番号 #26 にふさわしい機能と言えるでしょう.
そもそも UJFHack の「枠組み」を設計したのは,この機能と #25 の UJFHack-quick-quote の機能を分離するのが目的でした.
ページの取得に GM_xmlhttpRequest を用いているのでセキュリティに敏感な方はその引数を精査して下さい.
// ==UserScript== // @name UJFHack-page-button // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Add Page Buttons $Revision: 1.0 $ // @include https://forums.ubuntulinux.jp/viewtopic.php?* // ==/UserScript== var UJFHack = unsafeWindow.UJFHack; var ViewTopic = unsafeWindow.UJFHack.ViewTopic; var createButton = ViewTopic.createButton; var findNumber = UJFHack.findNumber; var findString = UJFHack.findString; var findAll = UJFHack.findAll; var findFirst = UJFHack.findFirst; var pagelink = findFirst('p[@class="pagelink conl"]', ViewTopic.inbox); var here = findNumber('strong', pagelink); var last = findFirst('a[position()=last()][starts-with(@href,"viewtopic.php?id=")]', pagelink); if (! last) { last = 1; } else { var path = last.href.replace(/\d+$/, ''); last = last.textContent; } var pageSelector = []; function selectPage(index) { for (var i = 0; i < pageSelector.length; ++ i) { pageSelector[i].selectedIndex = index; } } function slicePosts(html) { var b = html.lastIndexOf('<div', html.indexOf('class="blockpost')); var e = html.lastIndexOf('<div', html.lastIndexOf('class="postlinksb"')); return html.slice(b, e); } function createPage(nombre) { var div = document.createElement('div'); var inner = document.createElement('div'); function loadPage(detail) { div.innerHTML = slicePosts(detail.responseText); ViewTopic.revisePage(div); selectPage(nombre - 1); } function goLast(detail) { loadPage(detail); div.lastChild.previousSibling.scrollIntoView(true); } function request(func) { return function () { GM_xmlhttpRequest({method: 'GET', url : path + nombre, onload : func });}; } inner.className = 'blockpost'; inner.align = 'center'; inner.appendChild(createButton('ページ ' + nombre, request(loadPage))); inner.appendChild(createButton('ラスト ' + nombre, request(goLast))); div.appendChild(inner); return div; } var thePage = ViewTopic.thePage; var pageView = ViewTopic.pageView = document.createElement('div'); thePage.parentNode.insertBefore(pageView, thePage); for (var i = 1; i < here; ++ i) { pageView.appendChild(createPage(i)); } pageView.appendChild(thePage); for (var i = here + 1; i <= last; ++ i) { pageView.appendChild(createPage(i)); } var list = findAll('div/div[@class="inbox"]/p[@class="pagelink conl"]',ViewTopic.punviewtopic); if (here > last) { last = here; } for (var i = 0; i < list.snapshotLength; ++ i) { var node = list.snapshotItem(i); var select = pageSelector[i] = document.createElement('select'); var text = node.firstChild; function createOption(text) { var option = document.createElement('option'); option.textContent = text; select.appendChild(option); } function gotoPage() { var index = this.selectedIndex; selectPage(index); var top = index == last ? (-- index, false) : true; pageView.childNodes[index].scrollIntoView(top); } text.textContent = ':'; node.insertBefore(select, text); select.addEventListener('change', gotoPage, false); for (var j = 1; j <= last; ++ j) { createOption('ページ ' + j); } createOption('ラスト'); select.selectedIndex = here - 1; }
これで,以前に ubuntu-ja-* として投稿した機能はすべてカバーしました.あと一覧の残り3つと索引と番外を予定しています.
オフライン
※規約違反により追放されたユーザの投稿は、ログインユーザにのみ表示されます。
オフライン
UJFHack-scrollable-view : スクロールバー付きページビュー
トピックで投稿を表示する部分を「ページビュー」と呼ぶことにします.
ページビューが長くなると,-- 特に #26 の UJFHack-page-button を使うと益々長くなりますが,
その上下のナビゲーション(インデックスやログインへのリンク)が遠くなってアクセスしづらくなります.
ページビューの最大高を 40em に制限し,投稿がページビューに収まりきらないときはコードボックスと同様にスクロールバーが付くようにしました.
設定値はコードボックスの最大高 35em を参考に決めました.
解像度が 1024x768 の環境では FireFox のスクロールバーを少し動かせば上下どちらかのナビゲーションが目に入るぐらいの高さです.
// ==UserScript== // @name UJFHack-scrollable-view // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Scrollable View $Revision: 1.0 $ // @include https://forums.ubuntulinux.jp/viewtopic.php?* // ==/UserScript== var UJFHack = unsafeWindow.UJFHack; var ViewTopic = UJFHack.ViewTopic; var pageView = ViewTopic.pageView; var lastPage; function makeScrollable() { var style = pageView.style; lastPage = pageView.lastChild; lastPage.style.marginBottom = '-10px'; lastPage.lastChild.style.borderBottom = '0px'; pageView.firstChild.style.marginTop = '-1px'; style.borderBottom = style.borderTop = '1px solid rgb(129, 102, 71)'; style.marginBottom = '10px'; style.maxHeight = '40em'; style.overflow = 'auto'; var hash = location.hash; if (! hash) return; document.getElementById(hash.slice(1)).scrollIntoView(true); } UJFHack.hooks.push(makeScrollable); ViewTopic.pageRevisor.push(function (page) { if (page != lastPage) return; lastPage.lastChild.previousSibling.style.borderBottom = '0px'; });
オフライン
UJFHack-disable-smilies : 修整版 1.2 → #16 1.1 → #7 1.0
var の付け忘れを修正.やはり diff(#17) ではなく修正後の全文を再投稿します.
// ==UserScript== // @name UJFHack-disable-smilies // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Disable Smilies $Revision: 1.2 $ // @include https://forums.ubuntulinux.jp/viewtopic.php?* // @include https://forums.ubuntulinux.jp/post.php?* // ==/UserScript== if (location.pathname === '/post.php') { var hide = document.getElementsByName('hide_smilies')[0]; if (! hide) return; hide.checked = true; } else { var formsent = document.getElementsByName('form_sent')[0]; if (!formsent) return; var hide = document.createElement('input'); hide.type = 'hidden'; hide.name = 'hide_smilies'; hide.value = 1; formsent.parentNode.insertBefore(hide, formsent); }
オフライン
UJFHack-reverse-review : 逆順のレビュー
「クイック投稿」とプレビュー の knxg9001 さんの投稿 #8:
「b) クイック投稿は残しておく(現状のまま)」 がいいです。
「a) クイック投稿を無効にする」には反対です。
過去の投稿が下に表示されるのが嫌い(普段の表示と異なり馴染めない)のが理由です。
を読んで,書いてみたスクリプトです.
「返事を投稿」のページの過去の投稿のレビューを逆順にし,さらに返信投稿欄も最下段に置き「クイック投稿」の配置に近づけました.
UJFHack-keep-draft と UJFHack-quick-preview を併用すると「クイック投稿」と「返事を投稿」を入力済み文書を失わずに行き来できるので,統一感があるとレビューが読みやすくなります.
また,投稿予定の UJFHack-draggable-form は「クイック投稿」の配置を前提に仕組みを考えたので,返信フォームを「返事を投稿」でもドラッグしたいときは本スクリプトの使用が推奨です.
// ==UserScript== // @name UJFHack-reverse-review // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Reverse Post Review $Revision: 1.0 $ // @include https://forums.ubuntulinux.jp/post.php?* // ==/UserScript== var postreview = document.getElementById('postreview'); if (! postreview) return; var fragment = document.createDocumentFragment(); var child; while ((child = postreview.lastChild).nodeName !== 'H2') { fragment.appendChild(postreview.removeChild(child)); } child.firstChild.textContent = 'Topic review (oldest first)'; postreview.appendChild(fragment); var blockform = postreview.previousSibling.previousSibling; var punpost = postreview.parentNode; punpost.insertBefore(postreview, blockform); var postpreview = document.getElementById('postpreview') || document.getElementById('posterror'); if (postpreview) { punpost.insertBefore(postpreview, blockform); postpreview.scrollIntoView(true); } else { blockform.scrollIntoView(true); } var notice = unsafeWindow.UJFHack.findFirst('id("post")/div[@class="inform"]/fieldset/strong[1]', document); notice.parentNode.appendChild(notice);
// 改めてコードを読み返すと「枠組み」に入れる必要はほとんどありませんね…
オフライン
UJFHack-popup-search : 検索ボックスのポップアップ -- UJFHack-search-result #2 のの改案 --
UJFHack-search-result では常に検索ボックスを右上隅に出すようにしましたが,しばらく使ってみたところ UJFHack-quick-quote の「クイック引用」ボタンと重なったり,結構邪魔になる感じがしてきました.
右上に検索ボックスが有ることを知っている人を対象と割り切って,マウスを右上隅に持っていったら検索ボックスがポップアップするように変更,改名しました.
検索結果が別のタブで開くのはそのままです.
// ==UserScript== // @name UJFHack-popup-search // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Popup Search Box $Revision: 1.0 $ // @include https://forums.ubuntulinux.jp/* // ==/UserScript== var form = document.getElementById('cse-search-box'); if (!form) return; form.target = '_blank'; var searchbox = form.parentNode; var style = searchbox.style; function hideSearch () { style.visibility = 'hidden'; style.opacity = 0; } function showSearch () { style.visibility = 'visible'; style.opacity = 0.9; } style.top = '-2em'; style.zIndex = 50; style.position = 'fixed'; hideSearch(); var button = document.getElementsByName('sa')[0]; button.style.visibility = 'visible'; button.addEventListener('mouseover', showSearch, false); searchbox.addEventListener('mouseover',showSearch,false); searchbox.addEventListener('mouseout', hideSearch, false);
オフライン
UJFHack-draggable-form : ドラッグ可能なフォーム
マウスでドラッグ可能な何かが作りたくて,お遊びのつもりで作成したスクリプトですが,
「クイック投稿」とプレビュー の hito さんの投稿 #16:
・「このトピックに返事を投稿」のブロックをもっと下の方に追いやって、
・間を埋めることも兼ねて利用規約を表示し、
・さらに、『より高機能な「返事を投稿」からも投稿できます。プレビューも可能です』とか書いてしまう、
によるとクイック投稿の入力フォームはもっと下に移される可能性があるらしく,参照したい投稿とフォームが離れてしまいますので,投稿フォームが上下にドラッグできると便利かもしれません.
「このトピックに返信を投稿」の茶色のバーをドラッグして下さい.
// ==UserScript== // @name UJFHack-draggable-form // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Make Input Form Draggable $Revision: 1.0 $ // @include https://forums.ubuntulinux.jp/viewtopic.php?* // @include https://forums.ubuntulinux.jp/post.php?* // ==/UserScript== var UJFHack = unsafeWindow.UJFHack; var message = UJFHack.Post.message; if (!message) return; var blockform = UJFHack.findFirst('ancestor::div[@class="blockform"]', message); var bar = blockform.firstChild.nextSibling; var style = blockform.style; var limit = 0, delta = 0, top; function initialize() { limit = blockform.offsetParent.clientHeight - blockform.clientHeight; style.top = top = blockform.offsetTop + 'px'; style.width = '100%'; style.zIndex = 100; bar.removeEventListener('mousedown', initialize, false); } function stopMove() { style.cursor = 'auto'; document.body.removeEventListener('mousemove', doMove, false); document.body.removeEventListener('mouseup', endMove, false); } function doMove(event) { var dy; var y = event.clientY; event.stopPropagation(); event.preventDefault(); if ((dy = y - 32) < 0 || (dy = y - unsafeWindow.innerHeight + 32) > 0) { unsafeWindow.scrollBy(0, dy); delta += dy; } if ((y += delta) >= limit) { style.position = 'static'; style.opacity = 1; style.top = top; stopMove(); } else if (y > 0) { style.top = y + 'px'; } } function endMove(event) { style.opacity = 0.9; stopMove(); } function followMove(event) { var y = event.clientY + unsafeWindow.pageYOffset - blockform.offsetParent.offsetTop - bar.clientHeight; style.top = (y > 0 ? y : 0) + 'px'; delta = blockform.offsetTop - event.clientY; bar.removeEventListener('mouseout', followMove, false); } function beginMove(event) { style.opacity = 0.6; style.position = 'absolute'; style.cursor = 'move'; delta = blockform.offsetTop - event.clientY; document.body.addEventListener('mousemove', doMove, false); document.body.addEventListener('mouseup', endMove, false); bar.addEventListener('mouseout', followMove, false); event.stopPropagation(); event.preventDefault(); } bar.addEventListener('mousedown', initialize, false); bar.addEventListener('mousedown', beginMove, false);
post.php の URL も @include しているので,実際には「新しいトピックを投稿」と「返事を投稿」のページでもフォームがドラッグできます.
ただ,「新しいトピックを投稿」ではドラッグする意味は有りませんし,投稿フォームが最下段にあることを前提に作成したスクリプトなので,「返事を投稿」では UJFHack-reverse-review と併用しないと使い勝手は悪いと思います.
もっとも
「クイック投稿」とプレビュー の hito さんの投稿 #16:
# もっとも、フォーラムのベースになるソフトウェアそのもののバージョンアップを(将来)予定しているので、その時点ではまた何か別の問題が出てくるかな、とは思います。
とのことなので,この一連のスクリプトは無意味になるのでしょうね…
オフライン
予定のスクリプトはすべて投稿しました.お付き合い下さり有難うございました.
すこし時間を置いて,カスタマイズ例としての改訂版等(quick-quote の引用ボックスの見出しを #32 の書式にしたものとか)が投稿できたらと思います.
索引:
UJFHack-BEGIN : ルートオブジェクト #12
UJFHack-Post : Post オブジェクト #14
UJFHack-ViewTopic : ViewTopic オブジェクト #15
UJFHack-disable-smilies : 「スマイリーを表示しない」をチェック #29→#16→#7
UJFHack-draggable-form : ドラッグ可能なフォーム #32
UJFHack-keep-draft : 入力中の文章をログアウトまで保持 #18
UJFHack-linked-quote : リンク付き引用 #24
UJFHack-page-button : 25超投稿の1頁表示 #26
UJFHack-popup-search : 検索ボックスのポップアップ #31
UJFHack-privacy : 個人情報の簡易保護 #22
UJFHack-quick-preview : クイック投稿にプレビューボタン追加 #17
UJFHack-quick-quote : クイック投稿での引用機能 #25
UJFHack-reverse-review : 逆順のレビュー #30
UJFHack-scrollable-view : スクロールバー付きページビュー #28
UJFHack-END : フック関数を実行 #13
UJFHack-*は「枠組み」にあたるスクリプト
UJFHack-search-result は UJFHack-popup-search に改名
オフライン
※規約違反により追放されたユーザの投稿は、ログインユーザにのみ表示されます。
オフライン
テスト:
& > < "
& > < "
& > < "
オフライン
※規約違反により追放されたユーザの投稿は、ログインユーザにのみ表示されます。
オフライン
※規約違反により追放されたユーザの投稿は、ログインユーザにのみ表示されます。
オフライン
kiyop さん,報告有り難うございます.
なるほど,textarea から切り取った断片のエンティティがそのまま追加されるのか.
当面の回避策として,
sliceQuote 関数が return する値を次のように修正する必要があります.修整は return の行のみです.
function sliceQuote(html) { var b = html.indexOf(']', html.indexOf('name="req_message"')) + 1; var e = html.indexOf('</textarea>', b) - 1; return html.slice(b, e).replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); }
オフライン
※規約違反により追放されたユーザの投稿は、ログインユーザにのみ表示されます。
オフライン
UJFHack-quick-quote : 改訂版 2.0 → #25 旧版 1.0
// みなさん,GW と アップグレードが重なって忙しいようですね.
// ちなみに私は2台を 9.10 からアップグレード,1台にクリーンインストールしました.
// アップグレードした DynaBook の電源管理に問題は有るものの,概ね成功しました.
* 内部の,しかし重大な変更点
* 引用した投稿文を一時キャッシュして,同じページから同一投稿を引用したらキャッシュを使用するようにしました.
他のページに移動したときにそのキャッシュは廃棄します.
例えば,間違えてQTの引用パターンを挿入した後,改めてQUのパターンを挿入した場合,後者ではフォーラムとの通信は発生しません.
* #36 のバグ対策として,#38 の応急措置から,ダミーのテキストエリアで文字エンティティ(& 等)を変換するようにしました.
確か & > < " の4文字だけの対応で十分のはずと思うのだけれど,想像外のパターンがあるかも知れないので.
* その他,インターフェースの変更点
* 引用パターンの「カーソル位置への挿入」 (今までは常に末尾に追加)
* パターン一部変更 (O : オリジナルに近い引用,Tを含むパターン : 「の」でタグを一旦切るタイプに)
* 「ユーザ名挿入」ボタン
// ==UserScript== // @name UJFHack-quick-quote // @namespace http://localhost/ // @description Hack for Ubuntu Japanese Forum: Add Quick Quote Functions $Revision: 2.0 $ // @include https://forums.ubuntulinux.jp/viewtopic.php?* // ==/UserScript== var UJFHack = unsafeWindow.UJFHack; var ViewTopic = UJFHack.ViewTopic; var punviewtopic = ViewTopic.punviewtopic; var createButton = ViewTopic.createButton; var findString = UJFHack.findString; var findAll = UJFHack.findAll; var findFirst = UJFHack.findFirst; var topic = findString('ul/li[position()=last()]', ViewTopic.inbox).slice(3); var message = UJFHack.Post.message; function insertToMessage(text) { var t = message.value; var s = message.selectionStart; message.value = t.slice(0, s) + text + t.slice(message.selectionEnd); message.selectionStart = message.selectionEnd = s + text.length; } function insert(text) { return function () { insertToMessage(text); }; } if (message) { var list = findAll('div/div[@class="inbox"]/p[@class="postlink conr"]/a', punviewtopic); var i = 0; var reply = list.snapshotItem(0); var url = location.protocol + '//' + location.host + location.pathname + '?id=' + reply.href.replace(/.*=/, ''); var title = '[' + 'url=' + url + ']' + topic + '[/url]'; do { reply.parentNode.appendChild(createButton('タイトル挿入', insert(title))); } while (reply = list.snapshotItem(++ i)); } function sliceQuote(html) { var dummy = document.createElement('textarea'); function sliceQuote(html) { var b = html.indexOf(']', html.indexOf('name="req_message"')) + 1; var e = html.indexOf('</textarea>', b) - 1; dummy.innerHTML = html.slice(b, e); return dummy.value; } return sliceQuote(html); } function quickQuote(blockpost) { if (blockpost.nodeType !== unsafeWindow.Node.ELEMENT_NODE) return; var inbox = findFirst('div[@class="box"]/div[@class="inbox"]', blockpost); var dl = findFirst('div[@class="postleft"]/dl', inbox); var user = findString('dt/strong/a', dl); if (! user) return; var nbsp = String.fromCharCode(160); var postfootright = inbox.lastChild.previousSibling; var conr = findFirst('h2/span/span[@class="conr"]', blockpost); var post = conr.textContent.slice(0, -1); var link = '[' + 'url=' + conr.nextSibling.href + ']'; user += user === 'einundzwanzighundertsechs' ? nbsp : nbsp + 'さん'; var byuser = user + 'の投稿'; if (! message) { postfootright.textContent = link + byuser + nbsp + post + '[/url]'; return; } function withoutTitle(text) { return link + text + '[/url]'; } function withTitle(text) { return title + ' の ' + withoutTitle(text); } var quote; function quoteWith(tag) { return function () { insertToMessage(tag + quote + '\n'); }; } function withoutLink(text) { return quoteWith('[quote' + text + ']'); } function withLink(text) { return quoteWith('[quote]' + text + ':\n\n'); } function justDo(how) { how(); } function wrapper(how) { function memoize(detail) { quote = sliceQuote(detail.responseText); wrapper = justDo; how(); } var href = findFirst('ul/li[@class="postquote"]/a[starts-with(@href,"post.php")]', postfootright).href; GM_xmlhttpRequest({method : 'GET', url : href, onload : memoize}); } function insertQuote(how) { return function () { wrapper(how); }; } var menu = [ { text:'クイック引用' }, { text:'O', exec:insertQuote(withoutLink('=' + user)) }, { text:'Q', exec:insertQuote(withoutLink('')) }, { text:'T', exec:insert(withoutTitle(topic)) }, { text:'U', exec:insert(withoutTitle(byuser)) }, { text:'#', exec:insert(withoutTitle(post)) }, { text:'T#', exec:insert(withTitle(post)) }, { text:'U#', exec:insert(withoutTitle(byuser + nbsp + post)) }, { text:'QU', exec:insertQuote(withLink(withoutTitle(byuser))) }, { text:'TU', exec:insert(withTitle(byuser)) }, { text:'TU#', exec:insert(withTitle(byuser + nbsp + post)) }, { text:'QT#', exec:insertQuote(withLink(withTitle(post))) }, { text:'QU#', exec:insertQuote(withLink(withoutTitle(byuser + nbsp + post))) }, { text:'QTU', exec:insertQuote(withLink(withTitle(byuser))) }, { text:'QTU#', exec:insertQuote(withLink(withTitle(byuser + nbsp + post))) }, ]; var select = document.createElement('select'); for (var i = 0; i < menu.length; ++ i) { var option = document.createElement('option'); option.textContent = menu[i].text; select.appendChild(option); } select.addEventListener('change', function() { menu[this.selectedIndex].exec(); this.selectedIndex = 0; }, false); conr.appendChild(select); var dd = document.createElement('dd'); dl.insertBefore(dd, findFirst('dd[@class="usertitle"]',dl)); dd.appendChild(createButton('ユーザ名挿入', insert(user))); } function addQuoteButton(node) { for (var i = 0; i < node.childNodes.length; ++ i) { quickQuote(node.childNodes[i]); } } addQuoteButton(ViewTopic.thePage); ViewTopic.pageRevisor.push(addQuoteButton);
皆さん,他のメンバの「お名前」は失礼にならないように「手打ち」にしていますか? 私は,何といっても名前を間違えるのは失礼ですから,コピー&ペーストしていました.
さらにそれを1クリックでできるようにするのは,より失礼ということになるのかな? 気になる方は使わないでください.特定の一人を除いて「さん」を付けて挿入します.
オフライン
スクリプトを貼り付けていって、要望が出たら改善していくという手法はフォーラムという場所では向かないのではと思います。
特にこのフォーラムでは、新しい投稿は下または次のページへと続いていくので、このページを初めて見るユーザが目にするのは一番古いバージョンのスクリプトとなる、など。
コードホスティングサービス(例えばLaunchpadなど)を利用された方がいいと思います。
オフライン
pores_n さん,了解しました.
最後のスクリプトは通信のトラフィックを減らせるので,もし旧版を使っている人がいれば,できれば修正して使って欲しいと,
一応,投稿すべきかどうか悩んだ末に投稿したものです.
最初の投稿で予告したものだけ投稿できれば,元々止めるつもりだったので,目標は既に果たしています.
オフライン