MediaWiki:Gadget-pcomment.js
注意: 保存後、変更を確認するにはブラウザーのキャッシュを消去する必要がある場合があります。
- Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
- Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
- Internet Explorer / Microsoft Edge: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください
- Opera: Ctrl-F5を押してください
/***********************************************
* PukiWiki pcommentプラグイン風コメントボックス
*
* by pneuma01
***********************************************/
var i18n = {
summary_edit_with_pcomment: "PCommentでコメント",
send_button_caption: "コメントを追加"
};
$(function () {
const intersectionObserver = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
//画面内に入った
if (entry.isIntersecting) {
//コメント読み込み
loadComment(entry);
//監視対象の解除
intersectionObserver.unobserve(entry.target);
}
});
});
//監視開始
var observe_target = document.querySelectorAll(".pcomment-widget");
observe_target.forEach(function (element) {
intersectionObserver.observe(element);
});
});
//コメント読み込み
function loadComment(entry) {
//トークページ名を取得(存在しない場合でも)
var targetPage = entry.target.getAttribute("data-page") || mw.config.get('wgPageName');
var talkPage = new mw.Title( targetPage ).getTalkPage().getPrefixedText();
//トークページのwikitextを取得
$.when(new mw.Api().get({
action: 'query',
format: 'json',
titles: talkPage,
prop: 'revisions',
meta: 'tokens',
type: 'csrf',
rvprop: 'content|ids',
indexpageids: 1,
formatversion: '2',
rvslots: 'main'
}),
mw.user.getGroups()
).done(function (result, userGroups) {
result = result[0].query;
var page = result.pages[0];
//衝突防止のためにリビジョンを記録
if(page.revisions){
entry.target.dataset.revid = page.revisions[0].revid;
}
entry.target.dataset.csrftoken = result.tokens.csrftoken;
//非ログインユーザーにもコメントできるか?
var GuestUser = false;
if(entry.target.getAttribute("data-deny-guest")){
GuestUser = (userGroups.indexOf("user") == -1);
}
var wikitext = page.revisions && page.revisions[0].slots.main.content || "";
var lines = wikitext.split(/\r\n|\n|\r/);
var sections = [];
var lists = {};
var lists_count = 0;
var tree = {children: [], level: 0, text: "root"};
var curtree = tree;
var last_lvl = 1;
for (var i = 0; i < lines.length; i++) {
//セクションの位置を取得
if (lines[i].match(/^=+[^=]+=+$/)) {
sections.push({ index: sections.length, line: i, title: lines[i].match(/^=+([^=]+)=+$/)[1].trim() });
continue;
}
//*(<li>)の行番号を取得
if (lines[i].match(/^\*+/)) {
lists[i] = { index: lists_count++, line: i, section: sections.length - 1};
var m = lines[i].match(/^(\*+)/);
var astarisk = m[1];
//ツリーの始まりを記憶
if(astarisk.length == 1){
lists[i].toplevel = true;
}
//ツリーを記憶
{
var treedata = {line: i, level: astarisk.length, children: []};
if(astarisk.length < last_lvl){
do{
curtree = curtree.parent;
} while (curtree && curtree.level >= astarisk.length);
treedata.parent = curtree;
}
if(astarisk.length > last_lvl){
curtree = curtree.children[curtree.children.length-1];
}
treedata.parent = curtree;
curtree.children.push( treedata );
lists[i].tree = treedata;
}
last_lvl = astarisk.length;
}
}
//セクションがまだ存在しないならデフォルト
if (sections.length == 0) {
sections.push(
{ index: 0, line: -1, title: "PComment" }
);
}
//引数を取得
var sectionName = entry.target.getAttribute("data-section") || sections[sections.length - 1].title;
var sectionId = sections.length - 1;
var max = entry.target.getAttribute("data-max") || 10;
//引数の名前からセクションを特定する
for (i = 0; i < sections.length; i++) {
if (sections[i].title == sectionName) {
sectionId = sections[i].index;
break;
}
}
//次のセクションの先頭行を取得(セクション範囲識別に使用)
var nextSectionLine = lines.length;
if (sectionId > -1 && sections.length - 1 > sectionId) {
nextSectionLine = sections[sectionId + 1].line;
}
//コメントツリーの最後の行を取得するための関数
function getLast(obj){
if(obj.children.length){
return getLast(obj.children[obj.children.length -1]);
}
return obj.line;
}
//wikitextを指定レス分で抽出する
var new_lines = [];
var count = 1;
for (i = lines.length - 1; i >= 0; i--) {
//関係ないセクションはスキップ
if (i <= sections[sectionId].line || i >= nextSectionLine) {
continue;
}
var line = lines[i];
if (!GuestUser && lists[i]) {
//リスト構文の先頭にラジオボタン(予定地)を挿入する
var m = line.match(/^(\*+)([^\*].+)$/);
var astarisk = m[1];
var comment = m[2];
//最後のレスの行を取得
var lastlinenum = i;
if(lists[i].tree){
lastlinenum = getLast(lists[i].tree);
}
line = astarisk + "<span class='pcomment-radio' data-group='sec" + lists[i].section + "' data-line='" + lastlinenum + "' data-level='" + astarisk + "'></span>" + comment;
if (lists[i].toplevel) {
count++;
}
}
new_lines.push(line);
//指定のレス数に達したのでループを抜ける
if (count > max) break;
}
//抜き取った行をwikitextに変換する
wikitext = new_lines.reverse().join("\n");
// APIでWikitextをパースする
new mw.Api().parse(wikitext).done(function (data) {
//htmlを生成
var $html = $(data).removeClass('noscript');
//OOUIのラジオボタンを生成して挿入
$html.find("span.pcomment-radio").each(function () {
var val = {
line: $(this).attr('data-line'),
level: $(this).attr('data-level')
};
$(this).replaceWith(
new OO.ui.RadioInputWidget({ name: $(this).attr('data-group'), value: JSON.stringify(val) }).$element
);
});
//テキストボックスと送信ボタンを追加
if(!GuestUser){
var $text = new OO.ui.TextInputWidget();
var $button = new OO.ui.ButtonWidget({ label: i18n.send_button_caption, flags: ["primary", "progressive"] });
$html.append(
$("<div>", { class: "pcomment-toolbar", "data-section-end": nextSectionLine}).append(
$text.$element.addClass('pcomment-text').css("display", "inline-block"),
$button.$element.on('click', function(){
var selected = $(this).parents(".pcomment-widget").find("input:checked");
var section_end = $(this).parents(".pcomment-toolbar").attr("data-section-end");
var text = $(this).parent().find('.pcomment-text input').val();
if(text.trim().length == 0) {
//空送信を防止する
return;
}
$text.setDisabled(true);
$button.setDisabled(true);
$(this).parents(".pcomment-toolbar").prepend(
new OO.ui.ProgressBarWidget({ progress:false }).$element.addClass("oo-ui-pendingElement-pending")
);
var comment = {
targetPage: talkPage,
section_end: section_end,
wikitext: "* " + text + "--~" + "~" + "~" + "~",
entry: entry
};
if(selected.length){
var opt = JSON.parse(selected.val());
comment.line = opt.line;
comment.level = opt.level;
}
postComment(comment);
})
)
);
}
// できたHTMLの差し込み
$(entry.target).html("").append($html);
// 他のスクリプトへ読み込み完了通知
mw.hook('wikipage.content').fire($(entry.target));
});
});
}
function getRevIndex(revisions, revid){
for(var i=0; i<revisions.length; i++) {
if(revisions[i].revid == revid){
return i;
}
}
return -1;
}
//コメント追加用
function postComment(option) {
var query = {
action: 'query',
format: 'json',
prop: 'revisions',
titles: option.targetPage,
rvprop: 'content|ids',
rvslots: 'main',
indexpageids: 1,
formatversion: '2'
};
var oldrevid = option.entry.target.dataset.revid;
//元の版で取得
if(oldrevid) {
query.rvendid = oldrevid;
}
//最後のページ内容を取得
new mw.Api().get(query).done(function (result) {
result = result.query;
var page = result.pages[0];
var oldText = "";
var currevid = page.revisions && page.revisions[0].revid;
var linenum = option.line && option.line*1;
var section_end = option.section_end && option.section_end*1;
if(page.revisions){
oldText = page.revisions[0].slots.main.content;
}
//行で分割する
var new_lines = oldText.split(/\r\n|\n|\r/);
//リビジョンが更新されていた場合、該当する行番号へ更新
if(currevid != oldrevid) {
var index = getRevIndex(oldrevid);
oldText = page.revisions[ index ].slots.main.content;
var old_lines = oldText.split(/\r\n|\n|\r/);
var target_line;
if(option.line) {
target_line = old_lines[ linenum ];
linenum = new_lines.indexOf( target_line );
}
if(option.section_end){
if(section_end < old_lines.length - 2){
target_line = old_lines[ section_end + 1 ];
section_end = new_lines.indexOf( target_line ) - 1;
} else {
section_end = old_lines.length - 1;
}
}
}
if(linenum){
//レスする行が指定されているならコメントを挿入
new_lines.splice(linenum + 1, 0, option.level + option.wikitext);
} else if(section_end){
//レス行未指定
new_lines.splice(section_end, 0, option.wikitext);
} else {
new_lines.push(option.wikitext);
}
var newText = new_lines.join("\n");
//あたらしい内容を送信する
new mw.Api().post({
action: 'edit',
title: option.targetPage,
text: newText,
summary: i18n.summary_edit_with_pcomment,
token: option.entry.target.dataset.csrftoken
}).always(function () {
loadComment(option.entry);
});
});
}