MediaWiki:Common.js/titleChecker.js
/////////////////////////////////////////////////////////////////
// 記事名チェッカ
//   1.0 written by Tietew and Hatukanezumi
//
// See .
/////////////////////////////////////////////////////////////////
mw.loader.using(['mediawiki.util', 'mediawiki.template.mustache', 'oojs-ui-core', 'oojs-ui-widgets']).done(function() {
/** mw.config の内容を保持 */
var conf = mw.config.get([
'wgArticleId',
'wgArticlePath',
'wgIsRedirect',
'wgPageName',
'wgServer',
'wgTitle',
'skin'
]);
/** 記事名チェッカによる処理を適用しない記事名のリスト (除外リスト)。 */
var EXCLUDED_TITLES = [
// 項目名, ...
];
// 記事ごとの無効化。除外リストにあれば検査をしない。
if ($.inArray(EXCLUDED_TITLES, conf.wgTitle) >= 0 ||
$.inArray(EXCLUDED_TITLES, conf.wgTitle.replace(/_/g, ' ')) >= 0) {
return;
}
//警告レベル
var PERMIT = 0; //許可する
var WARN = 1;   //警告する
var DENY = 2;   //拒否する
/** 新規作成記事であるかどうか */
var isNewArticle = (conf.wgArticleId === 0);
/**
* テスト項目の定義
* 以下のパラメータを持つオブジェクトの配列。
*   pattern: テストする正規表現のパターン
*   level:   警告レベル (PERMIT|WARN|DENY)
*   message: 不合格時の表示テキスト
*   guides:  不合格時に表示するガイドラインの配列
*   skip:    特定条件で検査をスキップする場合 true を設定する
*/
var tests = [
/*
* 書式の検査
*/
// 記事名の全体を「」または『』などでくくっているもの、ないしは、その後に曖昧さ回避の括弧があるもの。
{
pattern: '^[「『].*[」』]([ _]+[(][^)]+[)])?$',
level:   WARN,
message: '記事名が鈎括弧でくくられています。芸術作品のタイトルは鈎括弧でくくるべきではありません(正式名称に鉤括弧を含むものは例外です)。',
guides:  ["Wikipedia:即時削除の方針#リダイレクト", "Wikipedia:記事名の付け方#小説・詩・映画・舞台・音楽・絵画など芸術作品のタイトル"],
skip:    false
},
// 記事名の末尾の括弧書きに半角の小括弧(丸括弧)を使用する場合に、左括弧の前に半角スペースがないもの。
// 注: 入れ子は2重まで。
{
pattern: '[^ ][(]([(][^()]*[)]|[^()])*[)]$',
level:   WARN,
message: '記事名の最後の左括弧の前に半角スペースがありません: %s。曖昧さ回避の括弧である場合は、括弧の前に半角スペースを入れてください。名称自体に括弧を含んでいる場合はこの限りではありません。',
guides:  ["Wikipedia:即時削除の方針#リダイレクト", "Wikipedia:曖昧さ回避#曖昧さ回避の種類と方法"],
skip:    false
},
// 記事名に半角の小括弧(丸括弧)を使用する場合に、括弧の左右が対称でないもの。
// 注: 入れ子は2重まで。
{
pattern: '[(]([(][^()()]*[)]|[(][^()()]*[)]|[^()()])*[)]|[(]([(][^()()]*[)]|[(][^()()]*[)]|[^()()])*[)]',
level:   WARN,
message: '括弧の左右が対称ではありません: %s。両方を、半角括弧か全角括弧に統一してください。',
guides:  ["Wikipedia:即時削除の方針#リダイレクト"],
skip:    false
},
/*
* 使用文字種の検査
*/
// その他のガイドライン等 (警告)
{
pattern: '[\uFF5E]+',
level:   WARN,
message: '全角チルダを含んでいます。この文字は、一部の環境で正しく表示されません。波ダッシュ (〜) か、できればハイフンマイナス (-) を使ってください。波ダッシュを使った記事名へのリダイレクトを作成しようとしている場合は、この限りではありません。',
guides:  ["Wikipedia:表記ガイド#波ダッシュ"],
skip:    conf.wgIsRedirect // リダイレクトなら全角チルダを許容
},
];
/**
* 記事名に対してテスト項目1つの結果を返す
* 合格していれば null を返す
* @param {string} title 記事名
* @param pattern: string, level: number, message: string, guides: string[], skip: boolean test 上記テスト項目の要素
* @returns {level: number, warning: {message: string, guides: string[]}, forRedirect: boolean}|null}
*/
var doTest = function (title, test) {
if(test.skip) return null;
var reg = new RegExp(test.pattern, "g");
var m = title.match(reg);
if(!m) return null;
var message = test.message.replace("%s", m.join(" ")).replace("%%", "%");
var forRedirect = false;
if (test.guides.length > 0 && conf.wgIsRedirect) {
forRedirect = (test.guides.indexOf('Wikipedia:即時削除の方針#リダイレクト') >= 0);
}
return {
level: test.level,
warning: {
message: message,
guides: test.guides
},
forRedirect: forRedirect
};
};
/**
* 警告文を画面に挿入する
* @param {HTMLElement} insertRef 警告文の挿入基点となるHTML要素
* @param {message: string, guides: string[]}[]} warnings 警告内容
* @param {boolean} forRedirect リダイレクトの即時削除対象でありうるかどうか
*/
var insertWarnings = function(insertRef, warnings, forRedirect) {
/** 記事名からウィキ内の記事URIを取得する */
var getUri = function(title) {
return conf.wgServer +
conf.wgArticlePath.replace('$1', mw.util.wikiUrlencode(title));
};
/** Wiki内のリンクを作成 */
var wl = function(title, alias) {
title = title.replace(/_/g, ' ');
var uri = getUri(title);
return '<a>' +
(alias || title) + '</a>';
};
var templateBody = //
// warnings を加工: 提示ガイドライン名の配列を {title: string, uri: string} の配列にする
warnings.forEach(function(warning) {
warning.guides = warning.guides.map(function(guide, i) {
return {
title: guide,
uri: getUri(guide),
first: (i === 0) // 最初の要素をテンプレートに判別させるためのフラグ
};
});
});
var data = {
warnings: warnings,
forRedirect: forRedirect && !isNewArticle
};
$(insertRef).after( + Mustache.render(templateBody, data) + );
};
/** 編集UIを使用不可にする */
var disableEditUi = function() {
// 新規記事の場合は編集画面ごと隠す
if(isNewArticle) {
$('#editform').hide();
return;
}
var editUiIds = ['wpSummary', 'wpMinoredit', 'wpWatchthis', 'wpSave', 'wpPreview', 'wpDiff'];
editUiIds.forEach(function(id) {
var widgetId = id + 'Widget',
$widget = $('#' + widgetId);
if($widget.lenth) {
// OOjs UIでラップされていたらそちらのメソッドを使う
OO.ui.infuse($widget).setDisabled(true);
} else {
$('#' + id).prop('disabled', true);
}
});
$('#wpTextbox1').prop('readonly', true);
};
/**
* 警告文の挿入基点となる要素を取得する
* @returns {HTMLElement}
*/
var getInsertRefOfWarnings = function() {
var insertRef;
switch (conf.skin) {
case 'cologneblue':
insertRef = $('#specialpages')[0] ?
$('#topbar')[0] :
$('h1.pagetitle')[0].nextSibling;
break;
default:
insertRef = $('#jump-to-nav')[0] ||
$('#contentSub')[0];
}
return insertRef;
};
// ロード時処理: 検査の実行と不合格項目があれば警告の表示
$(function() {
// 全テストを実施し不合格項目を配列で取得する
var failures = tests.map(function(test) {
return doTest(conf.wgTitle, test);
}).filter(function(r) {
// 合格時はnullが返るので除外する
return r !== null;
});
// すべて合格なら終了
if(failures.length === 0) return;
// いずれかに拒否が含まれる場合編集を禁止する
if(failures.some(function(r) { return r.level >= DENY; })) {
disableEditUi();
}
var warnings = failures.map(function(r) { return r.warning; });
var forRedirect = failures.some(function(r) { return r.forRedirect; });
// 警告文をページに挿入する
var insertRef = getInsertRefOfWarnings();
insertWarnings(insertRef, warnings, forRedirect);
});
});