利用者:Sat.d.h./variant-character-selector.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を押してください
/**
* このスクリプトは[[s:ja:MediaWiki:Gadget-char-convert0.js]] (2015-10-24T06:17:57, UTC)、
* および[[s:ja:MediaWiki:Gadget-to-old-char0.js]] (2015-12-21T14:02, UTC) をもとに作成しています。
* ▽使い方:
* 文字列を選択すると、辞書ファイルをもとに変換テーブルを作成し、異体字候補を表示します。
* 文字をクリックすると、wikiPreviewおよびwpTextbox1において、該当文字が置換されます。
*/
(function() {
'use strict';
// 標準、利用者、ページ名前空間の編集ページのみに適用
var namespaceNum = mw.config.get('wgNamespaceNumber');
if ((namespaceNum !== 0 && namespaceNum !== 2
&& namespaceNum != 250) || !document.getElementById('editform')) {
return;
}
/**
* selected - 選択された文字列で置換対象(IVSを含む)
* searched - 旧字等の異体字を検索する際の検索対象文字、
* かつ異体字セレクタを付加する基底文字(IVSを除外)
* button - ボタン(button要素の場合は
* Google Chromeで正常に機能しないため、spanで代用)
*/
var dicData = {};
var flag = false;
var selected = '';
var searched = '';
var wpTextbox = document.forms.editform.wpTextbox1;
var previewText = document.getElementById('wikiPreview');
var mouse_x = 0;
var mouse_y = 0;
var menu = {};
var button = document.createElement('span');
button.className = 'button';
(function initialize() {
// CSSファイル読み込み
var style = document.createElement('link');
style.href = '/w/index.php?title=User:Sat.d.h./for-variant-character-selector.css&action=raw&ctype=text/css';
style.rel = 'stylesheet';
style.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(style);
createDic();
// 切替えスイッチ
$('#p-namespaces > ul')
.append($('<li><span><a>異体字OFF</a></span></li>').attr({'id':'var-swich'}));
$('#var-swich').click(function() {
if (flag) {
flag = false;
$('#var-swich a').text('異体字OFF');
} else {
flag = true;
$('#var-swich a').text('異体字ON');
}
});
// 選択文字列の取得およびメニューの表示
wpTextbox.addEventListener('select', function(e) {
if (!flag) return;
var start = wpTextbox.selectionStart;
selected = wpTextbox.value.substr(start, 8);
if (start === wpTextbox.selectionEnd || selected.substr(0, 1).match(/[^\u3400-\uFAFF]/)) return;
document.addEventListener('mousemove', function f(e) {
mouse_x = e.clientX;
mouse_y = e.clientY;
document.removeEventListener('mousemove', f, false);
}, false);
charType ();
showMenu ();
}, false);
previewText.addEventListener('mouseup', function(e) {
if (!flag) return;
if (document.getSelection().toString() === '' || selected.substr(0, 1).match(/[^\u3400-\uFAFF]/)) return;
selected = document.getSelection().toString();
mouse_x = e.clientX;
mouse_y = e.clientY;
charType ();
showMenu ();
}, false);
}());
// 辞書データの作成
function createDic() {
var dfd = new $.Deferred();
/**
* 辞書データの取得
* 現在は[[s:ja:利用者:CES1596/辞書]]を利用しておりますが、
* 自前の辞書データを作成する予定です。
*/
(function getDic() {
/**
* pageids - 辞書のページID:'18270'
* dAstr - データ中のマーカ
*/
var pageids = '18270';
var dAstr = '*';
$.ajax({
type: 'GET',
scriptCharset: 'utf-8',
dataType: 'jsonp',
url: 'https://ja.wikisource.org/w/api.php?action=query&prop=revisions&rvprop=content&format=json&pageids='+pageids,
timeout: 5000,
})
.done(function(json) {
dicData = json.query.pages[pageids].revisions[0][dAstr];
dfd.resolve();
})
.fail(function() {
alert('辞書読取エラー');
});
}());
// データの整形
dfd.promise().done(function arrangeData() {
// console.log('deferred done');
var lines = dicData.split('\n');
var strD =[];
for (var i = 1; i < lines.length-1; i++) {
if (lines[i].match(/\*\s*([\S]*)\s*[::→]\s*([\S]*)/)) {
if (strD.length) strD += ', ';
strD += '\"'+RegExp.$2+'\":\"'+RegExp.$1+'\"';
}
}
dicData = JSON.parse('{' + strD + '}');
});
}
// 文字種ごとにsearchedとselectedを決定
function charType() {
if (selected.substr(0,3).match('&#x')) {
// 数値文字参照(主にCJK互換漢字)
searched = unescape('%u' + selected.substr(3, 4));
selected = selected.substr(0, 8) ;
} else if (selected.substr(0, 4).match(/[\uD800-\uDBFF][\uDC00-\uDFFF]\uDB40[\uDD00-\uDDEF]/)) {
// サロゲートペア+IVS
searched = selected.substr(0, 2);
selected = selected.substr(0, 4);
} else if (selected.substr(1, 2).match(/\uDB40[\uDD00-\uDDEF]/)) {
// IVS
searched = selected.substr(0, 1);
selected = selected.substr(0, 3);
} else if (selected.substr(0, 1).match(/[\uD800-\uDBFF]/)) {
// サロゲートペア
searched = selected.substr(0, 2);
selected = searched;
} else {
// その他の文字(一般的な文字)
searched = selected.substr(0, 1);
selected = searched;
}
}
// メニュー本体
function showMenu() {
// リセット
if (menu) {
if (menu.parentNode) document.body.removeChild(menu);
}
createFrame();
// 各種の文字一覧の作成
cjk();
ivs();
otherChar();
}
// 外部レイアウトの設定
function createFrame() {
var content = {};
var size = '';
var close = {};
menu = document.createElement('form');
menu.className = 'variantchar-menu';
menu.id = 'variantCharMenu';
// メニュー表示位置
content = document.body;
size = document.defaultView.getComputedStyle(content, null).fontSize;
size = parseFloat(size);
switch (true) {
case window.innerWidth <= size * 30 + 2:
menu.style.left = 0;
break;
case window.innerWidth <= mouse_x + (size * 30 + 2):
menu.style.right = 0;
break;
default:
menu.style.left = mouse_x + 'px';
break;
}
switch (true) {
case window.innerHeight <= size * 30 + 2:
menu.style.top = 0;
break;
case window.innerHeight <= mouse_y + (size * 30 + 2):
menu.style.bottom = 0;
break;
default:
menu.style.top = mouse_y + 'px';
break;
}
// 終了処理(閉じるボタン・メニューのダブルクリック・Escキー)
close = button.cloneNode(true);
close.className += ' close';
close.insertAdjacentHTML('afterbegin', '×');
close.addEventListener('click', function() { document.body.removeChild(menu); }, false);
menu.appendChild(close);
menu.addEventListener('dblclick', function() { document.body.removeChild(menu); }, false);
document.addEventListener('keydown', function(e) {
if (e.keyCode === 27) document.body.removeChild(menu);
}, false);
document.body.appendChild(menu);
}
// CJK統合・互換漢字を表示
function cjk() {
var i = 0;
var cjkMenu = {};
var cjkSet = {};
var cjkChar = '';
var cjkCode = '';
cjkMenu = createPanel (cjkMenu, 'CJK');
cjkCode = getUnicode(searched);
cjkSet[cjkCode] = searched;
Object.keys(dicData).forEach(function(key) {
var value = dicData[key];
if (selected === key || value.indexOf(selected) !== -1) {
// console.log('代替候補:' + key + ' → ' + value);
cjkCode = getUnicode(key);
cjkSet[cjkCode] = key;
while (value.substr(i, 1)) {
if (value.substr(i, 3).match('&#x')) {
cjkChar = unescape('%u' + value.substr(i+3, 4));
cjkCode = getUnicode(cjkChar);
cjkSet[cjkCode] = cjkChar;
} else if (!value.substr(i, 1).match(/[
-Fa-f0-9;g]/)) {
cjkChar = value.substr(i, 1);
cjkCode = getUnicode(cjkChar);
cjkSet[cjkCode] = cjkChar;
}
i++;
}
}
});
createItem (cjkMenu, cjkSet);
}
// 受け取った文字のユニコードを計算
function getUnicode(char) {
var surrogate1 = '';
var surrogate2 = '';
var unicode = escape(char);
if (char.match(/[\uD800-\uDBFF]/)) {
surrogate1 = unicode.slice(2,6);
surrogate1 = parseInt(surrogate1,16);
surrogate2 = unicode.slice(-4);
surrogate2 = parseInt(surrogate2,16);
unicode = 0x10000 + (surrogate1 - 0xD800) * 0x400 + (surrogate2 - 0xDC00);
} else {
unicode = unicode.slice(-4);
}
unicode = 'U+' + unicode.toString(16).toUpperCase();
return unicode;
}
// IVS文字を表示
function ivs() {
var i = 0;
var ivsMenu = {};
var ivsSet = {};
var ivsName = '';
var ivsChar = '';
ivsMenu = createPanel (ivsMenu, 'IVS');
for (i = 0; i < 32; i++) {
ivsChar = i.toString(16);
ivsChar = ('0' + ivsChar).slice(-2);
ivsName = 'U+E01' + ivsChar.toUpperCase();
ivsChar = '%uDB40%uDD' + ivsChar;
ivsChar = searched + unescape(ivsChar);
ivsSet[ivsName] = ivsChar;
}
createItem (ivsMenu, ivsSet);
}
// その他の文字用入力フォームを表示
function otherChar() {
var otherMenu = {};
var charInput = {};
var startReplace = {};
otherMenu = createPanel (otherMenu, 'その他');
charInput = document.createElement('input');
charInput.id = 'charInput';
charInput.type = 'search';
otherMenu.appendChild(charInput);
startReplace = button.cloneNode(true);
startReplace.insertAdjacentHTML('afterbegin', '置換');
startReplace.addEventListener('click', replaceChar, false);
otherMenu.appendChild(startReplace);
}
// 文字種ごとにパネルを作成
function createPanel(panel, type) {
var heading = {};
panel = document.createElement('div');
heading = document.createElement('h2');
heading.insertAdjacentHTML('afterbegin', type);
panel.appendChild(heading);
menu.appendChild(panel);
return panel;
}
// 各文字の入力ボタンを作成
function createItem(panel, charSet) {
var itemFrame = {};
var variantCaption = {};
var variantButton = {};
var item = {};
itemFrame = document.createElement('span');
itemFrame.className = 'item-frame';
variantCaption = document.createElement('span');
variantCaption.className = 'variant-caption';
variantButton = button.cloneNode(true);
variantButton.className += ' variant-button';
itemFrame.appendChild(variantCaption);
itemFrame.appendChild(variantButton);
Object.keys(charSet).forEach(function(key) {
item = itemFrame.cloneNode(true);
// variantCaption
item.children[0].insertAdjacentHTML('afterbegin', key);
// variantButton
item.children[1].insertAdjacentHTML('afterbegin', charSet[key]);
item.children[1].addEventListener('click', replaceChar, false);
panel.appendChild(item);
});
}
// 置換・メニュー終了
function replaceChar(e) {
var newChar = '';
if (e.target.textContent === '置換') {
newChar = document.forms.variantCharMenu.charInput.value;
} else {
newChar = e.target.textContent;
}
previewText.innerHTML = previewText.innerHTML.replace(new RegExp(selected, 'g'), newChar);
wpTextbox.value = wpTextbox.value.replace(new RegExp(selected, 'g'), newChar);
document.body.removeChild(menu);
}
}());