MediaWiki:Gadget-vpTagHelper.js
/*
* vpTagHelper.js
* 井戸端タグ支援ツール
*
* 井戸端サブページの編集画面に、タグ検索・編集機能を追加する
*/
if (
mw.config.get('wgPageName').indexOf('Wikipedia:井戸端/subj/') === 0 && //井戸端サブページ、かつ
(mw.config.get('wgAction') == 'edit' || mw.config.get('wgAction') == 'submit') && //編集・プレビュー・差分確認時のみで
(!jQuery('[name=wpSection]').length || jQuery('[name=wpSection]').val().match(/^0?$/)) //(導入部除き)節編集モードではない
) {
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'mediawiki.template.mustache']).then(function () {
/** タグ一覧のロード・保持・抽出を管理するオブジェクト */
var TagKeeper = (function () {
var TEMPLATE_EMBEDDED_FOR_SEARCH = 'Template:井戸端タグカテゴリ'; //このテンプレートの呼出元が完全リストとなる
var CATEGORY_PREFIX = 'Category:井戸端の話題/'; //見つかったタグカテゴリから取り除くべきプレフィクス
var SEARCH_LIMIT = 500; //1回の検索数上限
var allTags = [];
var currentTags = [];
var recommendTags = [];
var api = new mw.Api();
/** APIのクエリ結果から全タグの配列を作成、allTagsに格納する */
function loadAllTagsFromAPI(callback, continueInfo) {
var apiParams = {
format: 'json',
action: 'query',
list: 'embeddedin',
einamespace: 14,
eilimit: SEARCH_LIMIT,
eititle: TEMPLATE_EMBEDDED_FOR_SEARCH,
continue: ,
};
$.extend(apiParams, continueInfo);
api.get(apiParams).done(function (data) {
if (!data.query || !data.query.embeddedin) return;
var tags = data.query.embeddedin.map(function (ent) {
return ent.title.substring(CATEGORY_PREFIX.length);
});
allTags = allTags.concat(tags);
//1回のロード件数 (SEARCH_LIMIT) より多ければ続きをロードする
if (data['continue']) {
loadAllTagsFromAPI(callback, data['continue']);
} else {
//ロード終了後処理
callback();
}
});
}
return {
/** 全タグのロードとページ解析 完了後コールバック実行 */
initialize: function (pageText, callback) {
loadAllTagsFromAPI(function () {
TagKeeper.parse(pageText);
callback();
});
},
/** タグの選択状態切り替え */
toggle: function (tag) {
var i = currentTags.indexOf(tag);
var toggledOn = i < 0;
if (toggledOn) currentTags.push(tag);
else currentTags.splice(i, 1);
return toggledOn;
},
/** 現在のタグ */
current: function () {
return currentTags;
},
/** 使用可能なすべてのタグ */
all: function () {
return allTags;
},
/** 編集エリア内で見つかったタグ候補 */
recommend: function () {
return recommendTags;
},
/** クエリ文字列に中間一致するタグの配列を返す */
search: function (query) {
return allTags.filter(function (tag) {
return tag.toUpperCase().indexOf(query.toUpperCase()) >= 0;
});
},
/** 編集エリアのウィキテキストを解析する */
parse: function (pageText) {
//現在記入済みのタグを取得
var m = pageText.match(/\{\{vptag(?:\s*\|\s*([^{}]*?)\s*)?\}\}/i);
if (m && m[1] && m[1].length > 0) {
currentTags = m[1].split(/\s*\|\s*/);
}
pageText = pageText
.toUpperCase()
.replace(/\(UTC\)/g, ) //署名の(UTC)は拾わないように除去
.replace(//g, ); //コメント内を拾わない
//井戸端タグ以降を検索対象とする
var searchStart = pageText.indexOf(;
$textarea().val(wikiText.replace(reg, dest));
},
/** 要約欄テキストを取得する */
getSummary: function () {
return $('#' + ID_SUMMARY).val();
},
/** 要約欄にテキストを適用する */
applySummary: function (summary) {
$('#' + ID_SUMMARY).val(summary);
},
/** 所定の位置にタグ操作UIを挿入する */
appendConsole: function (console) {
$('#' + ID_INSERT_BEFORE).before(console);
},
};
})();
/** タグ操作UI */
var VpTagUI = (function () {
//このツールで定義するid/class
var ID_RECOMMEND_CONTAINER = 'vptag-recommend'; //タグ候補表示部のid
var ID_SEARCH_RESULT_CONTAINER = 'vptag-searchresult'; //検索結果表示部のid
var ID_SEARCH_INPUT = 'vptag-searchinput'; //検索文字入力欄のid
var ID_APPEND_BUTTON = 'vptag-append'; //タグ追加ボタンのid
var CLASS_TOGGLED = 'vptag-toggled'; //ボタントグル状態のclass
var CONTAINER_HTML = //ツール本体HTML
'\n' +
'<form><p>\n' +
'<label>井戸端タグ検索:</label>\n' +
'<input id="' +
ID_SEARCH_INPUT +
'" type="text" />\n' +
'<input type="text" style="position:absolute; visibility:hidden" />\n' + //dummy 誤送信防止
'<button>追加</button>\n' +
'</p></form>\n' +
'<p></p>\n' +
'<p>\n' +
'タグ候補:\n' +
'\n' +
'</p>\n' +
;
var tagButtonsTemplate =
+
'<a class="vptag-button' +
' ' +
CLASS_TOGGLED +
'/toggled">' +
'name' +
'</a> ' +
'/tags';
/** タグボタンの生成 */
function createTagButtons(tags) {
var data = {};
data.tags = tags.map(function (tag) {
return {
name: tag,
toggled: TagKeeper.current().indexOf(tag) >= 0,
};
});
return Mustache.render(tagButtonsTemplate, data);
}
/** タグボタンクリック時処理 */
function tagButtonOnclick(e) {
var tag = $(this).text();
var toggledOn = TagKeeper.toggle(tag);
PageUI.applyTags(TagKeeper.current());
$(this).toggleClass(CLASS_TOGGLED, toggledOn);
if (toggledOn) {
TagHistory.add(tag);
} else {
TagHistory.remove(tag);
}
PageUI.applySummary(TagHistory.generateSummary());
}
/** 検索結果を表示する */
function searchTags(query) {
$('#' + ID_SEARCH_RESULT_CONTAINER).html();
if (query === ) return;
$('#' + ID_SEARCH_RESULT_CONTAINER)
.empty()
.append(createTagButtons(TagKeeper.search(query)));
}
return {
/** 操作UIの生成・イベント設定 */
setup: function () {
//ツールフォームの生成・挿入
PageUI.appendConsole($(CONTAINER_HTML));
//イベント設定
$('#' + ID_SEARCH_INPUT).keyup(function (e) {
searchTags($(this).val());
});
$('#' + ID_APPEND_BUTTON).click(function (e) {
TagKeeper.toggle($('#' + ID_SEARCH_INPUT).val());
PageUI.applyTags(TagKeeper.current());
});
$('#' + ID_RECOMMEND_CONTAINER + ', #' + ID_SEARCH_RESULT_CONTAINER).on('click', 'a', tagButtonOnclick);
},
/** タグ候補を更新する */
refreshRecommend: function () {
$('#' + ID_RECOMMEND_CONTAINER)
.empty()
.append(createTagButtons(TagKeeper.recommend()));
},
};
})();
/* ページロード後処理 */
$(function () {
VpTagUI.setup();
TagKeeper.initialize(PageUI.getWikiText(), VpTagUI.refreshRecommend);
//プレビュー時、要約欄に記載済みの操作履歴を取得
TagHistory.parseSummary(PageUI.getSummary());
});
});
} //end if