へもかメソッド入力支援Greasemonkey

ブラウザからテキストエリアへ入力する際に、語尾に小さな「」「」「」を追加しやすくするためのGreasemonkeyスクリプトを書いてみました。

インストール先

hemoka.user.js

内容をご確認のうえ、よろしければ、どうぞご利用ください。

へもかメソッドについて

id:kokokubetaさんのエントリをご参照ください。

「首相、退陣か」「広告業界、崩壊も」「噂の二人、結婚へ」語尾にへ・も・かをついて曖昧にすることをへもかメソッドと呼びます(?)。よくスポーツ紙で見ますが、一般的な新聞でもあやふやな情報を出すときにはこの語尾になってたりするようです。さて、ネットでもこのへもかメソッドを使いたい(自信がないときとか)。今は「x」+「あ」で小さい「ぁ」が出るようになっているけど、同様の操作で小さい「へ」「も」「か」が出るようになってほしい。いや、ほしくない。

へもかキーが欲しい - kokokubeta;

ご利用方法

各種ブログサービスなどで、ブラウザからテキストエリア内に書き込むときに動作します。XまたはLキーのあとに「へ」「も」「か」を入力すると、smallタグを自動補完します。
また、語尾に助詞「へ」「も」「か」がつく単語がリストアップされます。小さな「へもか」にしたいときは該当するチェックボックスをチェックします。解除するとふつうにもどります。
単語が表示される灰色の領域は、マウスを押しながら移動させることができます。

注意事項

HTML形式を受けつけるテキストエリアのご利用を想定しています。

ソース

// ==UserScript==
// @name           hemoka
// @namespace      http://d.hatena.ne.jp/atkatanto/
// @include        http://*
// @include        https://*
// ==/UserScript==

var editor;

function seek(){
    if(editor) return;
    editor = document.getElementsByTagName('textarea')[0];
    if(editor){
        editor.addEventListener('keyup', parse, false);
        document.removeEventListener('DOMNodeInserted', seek, false);
    }
}
document.addEventListener('DOMNodeInserted', seek, false);
seek();

function parse(){
    editor.value = editor.value.replace(/ヵ|[xXxXlLlL]([へもか])/g, function(v){return '<small>' + (v[1] || 'か') + '</small>';});
    var text = editor.value;
    var str = text;
    var pos;
    var start = 0;
    var list = [];
    var regex = /(([一-龠々〆ヵヶ]+|[ぁ-んー]+|[ァ-ヴーヵヶ]+|[a-zA-Z]+|[a-zA-Z]+)?(?:([へもか])(?=[\s !?!?..。・…'"’”))」』,、]|$)|<small>[\s\n ]*([へもか])[\s\n ]*<\/small>))/;
    while(true){
        pos = str.search(regex);
        if(pos == -1) break;
        pos = pos + start;
        list.push({start:pos, word:RegExp.$2, hemoka:(RegExp.$3||RegExp.$4), small:!!RegExp.$4, original:RegExp.$1});
        start = pos + RegExp.$1.length;
        str = text.slice(start);
    }
    set(list);
}

var moving = false;
var baseX, baseY, pageX, pageY;
var panel;

function set(list){
    if(!panel){
        panel = document.createElement('div');
        with(panel.style){
            position  = 'absolute';
            width     = '200px';
            height    = '200px';
            overflow  = 'auto';
            margin    = '0px';
            border    = '0px';
            padding   = '10px';
            right     = '10px';
            top       = '10px';
            cursor    = 'move';
            zIndex    = '2147483647';
            textAlign = 'left';
            backgroundColor = '#ddd';
        }
        panel.addEventListener('mousedown', function(e){
            if(moving) return;
            e.preventDefault();
            with(getComputedStyle(panel, null)){
                baseX = parseInt(left.replace('px','')) || 0;
                baseY = parseInt(top.replace('px',''))  || 0;
            }
            pageX = e.pageX;
            pageY = e.pageY;
            moving = true;
        }, false);
        document.body.appendChild(panel);
    }
    var html = '<ol>';
    var data;
    var i;
    for(i=0; i<list.length; i++){
        data = list[i];
        html += '<li><input type="checkbox" ' + (data.small ? 'checked' : '') + '/> ' + data.word + data.hemoka + '</li>';
    }
    html += '</ol>';
    var words = document.createRange().createContextualFragment( html );
    var input = document.evaluate('descendant::*[local-name()="INPUT" or local-name()="input"]', words.firstChild, null, 7, null);
    for(i=0; i<input.snapshotLength; i++)with({j:i}){
        input.snapshotItem(i).addEventListener('click', function(){
            var data = list[j];
            var flag = this.checked;
            var text = editor.value;
            var delta = flag ? 15 : (data.word.length + 1 - data.original.length);
            var renewal = data.word + (flag ? ('<small>' + data.hemoka + '</small>') : data.hemoka);
            editor.value = text.substr(0, data.start) 
                         + text.slice(data.start).replace(data.original, renewal);
            list[j].original = renewal;
            for(var i=j+1; i<list.length; i++){
                list[i].start += delta;
            }
            if(!flag && (list[j].start + list[j].original.length == list[j+1].start)) parse();
        }, false);
    }
    panel.innerHTML = '<center><b><small>へもか</small>メソッド</b></center><hr/>';
    panel.appendChild( words );
}

document.addEventListener('mousemove', function(e){
    if(!moving) return;
    with(panel.style){
        left = (e.pageX - pageX + baseX) + 'px';
        top  = (e.pageY - pageY + baseY) + 'px';
        right = '';
    }
}, true);

document.addEventListener('mouseup', function(e){
    if(!moving) return;
    moving = false;
}, true);