2010年09月23日 02:00 [Edit]

DHTML - 構造化テキストは構造化するのがやっぱ正しい

うーん、気に食わない。

404 Blog Not Found:構造化テキストの間違ったエスケープ手法について
make_link = function(href, text) {
    var a = document.createElement('a');
    a.href = href;
    a.innerHTML = text;
    var div = document.createElement('div');
    div.appendChild(a);
    return div.innerHTML;
};

何が気に食わないって、折角作ったDOMという構造をベタテキストに戻しちゃうところ。

脱構造しない例

こうすればよいのではないか。

(function(){
var re_http =       '(?:https?://\\S+)',
    re_user =       '(?:[@][0-9A-Za-z_]{1,15})',
    re_hashtag =    '(?:[#]\\S+)',
    re_tweet   = new RegExp(
        '('+[re_http, re_user, re_hashtag].join('|')+')', 'g'
    ); 

var make_link = function(href, txt) {
    var a = document.createElement('a');
    a.href = href;
    a.appendChild(document.createTextNode(txt));
    return a;
};

parse_tweet = function(str) {
    var i, l, e, t, d = document,
        ts = str.split(re_tweet),
        fr = d.createDocumentFragment();
    for (i = 0, l = ts.length; i < l; i++, e = null) {
        t = ts[i];
        if (t.match(re_http)) {
            fr.appendChild(make_link(t, t));
        }else if (t.match(re_user)) {
            fr.appendChild(d.createTextNode('@'));
            t = t.replace(/^[@]/,'');
            fr.appendChild(make_link('http://twitter.com/' + t, t));
        }else if (t.match(re_hashtag)) {
            fr.appendChild(make_link(
                'http://twitter.com/search?q=' + encodeURIComponent(t), t
            ));
        }else if (t.length) { /* t can be empty */
            fr.appendChild(d.createTextNode(t));
        }
    }
    return fr;
};
})();
Tweet:
Parsed:
(function($){
    parsetw = function(){
        cleartw();
        $('ptweet').appendChild(parse_tweet($('tweet').value));
    };
    cleartw = function(){ $('ptweet').innerHTML = '' };
})(
    function () { return document.getElementById.apply(document, arguments); }
);

ここではjQueryなどを使わず、DOM coreのみで対処してみた。String.prototype.splitが正規表現、それもcapture入りのがクロスブラウザーできちんと動くかいまいちあやふやなのだけど、Firefox 3.6, Safari 5, Chrome 6, Opera 10.61で試した限りはきちんと動いている。

これで(document.createTextNode()の引数となるべき単なるテキストである)[<>&"]のエスケープも不要になったし、コードも短くなったし、いいことづくめ。

正しいアプローチその三は、不必要に脱構造しない

ということでOK?

Dan the Structured Programmer

追記:iPhone/iPadはOKなるも、IE8がNG。ぐぬぬ。前の記事はOKなのだが…

2記:これではいかに?String.prototype.splitを使わない版。

(function(){
var re_http =       '(?:https?://\\S+)',
    re_user =       '(?:[@][0-9A-Za-z_]{1,15})',
    re_hashtag =    '(?:[#]\\S+)',
    re_anychar =    '(?:[\\s\\S])',
    re_tweet_ns   = new RegExp(
        '('+[re_http, re_user, re_hashtag, re_anychar].join('|')+')', 'g'
    ); 

var make_link = function(href, txt) {
    var a = document.createElement('a');
    a.href = href;
    a.appendChild(document.createTextNode(txt));
    return a;
};

parse_tweet_ns = function(str) {
    var i, l, e, t, d = document,
        fr = d.createDocumentFragment();
    str.replace(re_tweet_ns, function(t) {
        if (t.match(re_http)) {
            fr.appendChild(make_link(t, t));
        }else if (t.match(re_user)) {
            fr.appendChild(d.createTextNode('@'));
            t = t.replace(/^[@]/,'');
            fr.appendChild(make_link('http://twitter.com/' + t, t));
        }else if (t.match(re_hashtag)) {
            fr.appendChild(make_link(
                'http://twitter.com/search?q=' + encodeURIComponent(t), t
            ));
        }else { /* t is a single char */
            fr.appendChild(d.createTextNode(t));
        }
        return t;
    });
    if (fr.normalize) fr.normalize();
    return fr;
};
})();
Tweet:
Parsed:
(function($){
    parsetw1 = function(){
        cleartw1();
        $('ptweet1').appendChild(parse_tweet_ns($('tweet1').value));
    };
    cleartw1 = function(){ $('ptweet1').innerHTML = '' };
})(
    function () { return document.getElementById.apply(document, arguments); }
);

IE8でも動くようにはなったが、ただでさえぐっと非効率になったところに、さらにIEに限ってDOM.normalize()できないので一文字ごとにtext nodeができてしまうことになるなあ。


この記事へのトラックバックURL

この記事へのトラックバック
先のエントリ「(Twitter の XSS 脆弱性に関連して) 構造化テキストの正しいエスケープ手法について」の続き。 弾さんが「404 Blog Not Found:DHTML - 構造化テキストは
String::Filter っていうモジュール書いた - 続: (Twitter の XSS 脆弱性に関連して) 構造化テキストの正しいエスケープ手法について【Kazuho@Cybozu Labs】at 2010年09月24日 07:31