#!/usr/bin/perl package MT::Plugins::TextHatena; use vars qw($VERSION); $VERSION = 0.01; use strict; use Jcode; use Text::Hatena; use MT; use MT::Blog; use MT::Entry; use MT::Template::Context; MT->add_text_filter(text_hatena => { label => 'MT::Text::Hatena', on_format => sub { my ($text, $ctx) = @_; if($text eq ''){ return ""; } my $entry = $ctx->stash('entry'); my $permalink = ""; if ($entry && $entry->id) { $permalink = $entry->permalink; } my $parser = Text::Hatena->new( permalink => $permalink, ); my $html = $parser->parse($text); parseLinks($parser->html); }, }); sub parseLinks{ my $source = shift; my @lines = split(/\n/, $source); foreach my $line(@lines){ # さまざまなパターンについて分岐 $line =~ s!( # src="URL", href="url" ((src|href|cite)=[\'\"](http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*[\'\"]) | # []URL[] (\[\](http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*\[\]) | # [URL] (\[(http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*(:image)?\]) | # [URL] (\[(http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*(:title=.*)?\]) | # [URL] ((http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*) | # [map:WORDs] #map:x139.6983y35.6514 #[map:渋谷区鉢山町] #[map:150-0035] #[map:株式会社はてな] (\[(map):[^\]]+\]) | # [search:WORDs] (\[(search):((keyword|question|asin):)?[^\]]+\]) | # [google:WORDs] (\[(google):((image|news):)?[^\]]+\]) | # [amazon:WORDs] (\[(amazon):[^\]]+\]) | # [rakuten:WORDs] (\[(rakuten):[^\]]+\]) | # id:hogehoge:yyyymmdd (id:\w+(:\d+)?) | # g:hugahuga:id:hogehoge:yyyymmdd (g:\w+(:id:\w+(:\d+)?)?) | # ?:id:hogehoge:〜 #a:id:sample #b:id:sample,b:id:sample:20050707,b:id:sample:favorite,b:id:sample:asin #d:id:hatenadiary,d:id:hatenadiary:20040510 #i:id:sample #r:id:sample #f:id:hatenadiary:20041007101545j:image,f:id:hatenadiary:20041007101545j:image:small #f:id:hatenadiary:20041007101545j:image:w50,f:id:hatenadiary:favorite ((a|b|i|r|f|map):id:\w+(:\w+)?) | # [keyword:WORDs] #[keyword:はてな] #[keyword:はてな:graph] #[keyword:はてな:graph:accessrank:3d] (\[(keyword):[^\]]+\]) | #[g:hatena:keyword:はてなダイアリー利用可能タグ] (\[g:\w+:(keyword):[^\]]+\]) | )!&toHtml($1)!igex; #idea:182,idea:182:title #graph:id:sample #[graph:id:sample:しなもんの体重] #[graph:id:sample:しなもんの体重:image] #jan:4981254610640 #ean:4981254610640 #jan:4981254610640:title #[jan:4981254610640:title=この商品] #jan:4981254610640:image #jan:4981254610640:barcode #id:hatenadiary:about #id:hatenadiary:archive #id:hatenadiary:archive:200508 #question:1096559040 #question:1096559040:title #question:1092057258:detail #uestion:1092057258:image #question:1092057258:image:small } join("\n",@lines); } sub toHtml{ my $line = shift; # src="URL", href="url" if($line =~ /(src|href|cite)=[\'\"](http|https|ftp|mailto):([^\x00-\x20()<>\x7F-\xFF])*[\'\"]/){ return $line; } # []URL[] if($line=~/\[\]((http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*)\[\]/){ return $1; } # [URL:image] if($line=~/\[((http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*)(:image)?\]/){ return qq(); } # [URL] if($line=~/\[((http|https|ftp|mailto|isbn|asin|ISBN|ASIN):([^\x00-\x20()<>\x7F-\xFF])*)(:title=(.*))?\]/){ return ($4 eq '') ? qq($1) : qq($5); } # [URL] if($line=~/((http|https|ftp|mailto):([^\x00-\x20()<>\x7F-\xFF])*)/){ return qq($1); } # [map:WORDs] #未対応:map:x139.6983y35.6514 #[map:渋谷区鉢山町],[map:150-0035],[map:株式会社はてな] if($line=~/\[(map:([^\]]+))\]/){ my $encoded = toUtf8($2); return qq($1); } # [search:WORDs] if($line=~/\[(search:((keyword|question|asin):)?([^\]]+))\]/){ my $u = 'http://search.hatena.ne.jp'; if($3 ne ''){ $u .= ($3 eq 'keyword') ? '/keyword' : ($3 eq 'question') ? '/questsearch' : ($3 eq 'asin') ? '/asinsearch' : ''; } else{ $u .= '/keyword'; } $u .= '?word=' . toEuc($4); return qq($1); } # [google:WORDs] if($line=~/\[(google:((image|news):)?([^\]]+))\]/){ my $u='http://www.google.com/search'; my $encoded = toEuc($4); if($2){ $u = ($3 eq 'image') ? 'http://images.google.com/images' : ($3 eq 'news') ? 'http://news.google.com/news' : ''; } return qq($1); } # [ASIN:xxx] if($line=~/((isbn|asin|ISBN|ASIN):(\w+)(:(detail|image)(:(small|large))?)?)/){ return qq($1); } # [amazon:WORDs] if($line=~/\[(amazon:([^\]]+))\]/){ my $encoded = toEuc($2); return qq($1); } # [rakuten:WORDs] if($line=~/\[(rakuten:([^\]]+))\]/){ my $encoded = toEuc($2); return qq($1); } # [keyword:WORDs] #(\[(keyword):[^\]]+\]) if($line=~/\[(keyword:([^\]]+))\]/){ my $encoded = toEuc($2); return qq($1); } # id:hogehoge:yyyymmdd #d:id:hatenadiary,d:id:hatenadiary:20040510 if($line =~ /((d:)?id:(\w+)(:(\d+))?)/){ return qq($1); } # g:hugahuga:id:hogehoge:yyyymmdd if($line =~ /(g:(\w+)(:id:(\w+)(:(\d+))?))/){ return qq($1); } #b:id:sample,b:id:sample:20050707,b:id:sample:favorite,b:id:sample:asin if($line =~ /\[?(b:id:(\w+)(:(\w+))?)\]?/){ return qq($1); } #b:keyword:xxx if($line=~/\[(b:keyword:([^\]]+))\]/){ my $encoded = toEuc($2); return qq($1); } #idea:999 if($line =~ /(idea:(\d+))/){ return qq($1); } #?:id:hogehoge:〜 if($line =~ /((a|b|d|f|i|r|f|map):id:(\w+))/){ return qq($1); } #f:id:hatenadiary:20041007101545j:image,f:id:hatenadiary:20041007101545j:image:small #f:id:hatenadiary:20041007101545j:image:w50,f:id:hatenadiary:favorite #[g:hatena:keyword:はてなダイアリー利用可能タグ] #(\[g:\w+:(keyword):[^\]]+\]) $line; } sub toEuc(){ my $src = shift; my $enc = Jcode->new($src)->euc; $enc =~ s/(\W)/'%' . unpack('H2', $1)/eg; $enc =~ tr/ /+/; $enc; } sub toUtf8(){ my $src = shift; my $enc = Jcode->new($src)->utf8; $enc =~ s/(\W)/'%' . unpack('H2', $1)/eg; $enc =~ tr/ /+/; $enc; } 1;