google の検索結果で、文字切りされているタイトルをフル表示する Greasemonkeyスクリプト
需要があるかは謎ですが、title属性にもフルのタイトルが入ってなくて微妙だと思ったので。
google の検索結果で、タイトルが長いと途中で文字切りされている場合があります。
例えば、このブログの記事だとこれとか。
こういう文字切りをしている場合は、リンクのtitle属性にはフルのタイトルが入っていて、
マウスオーバーでポップアップして全部見ることができるケースが多いですが、google はそうなっていません。
なので、マウスオーバー時点で ajaxでタイトルを取りにいって、フル表示するスクリプトを作りました。
show_full-title_for_google.user.js
【補足】
iPhone Safariだとaudio/videoタグのautoplay, autobuffer属性が効かない
Resources - Safari - Apple Developer の
User Control of Downloads Over Cellular Networks」の部分
In Safari on iPhone OS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, autobuffering and autoplay are disabled. No data is loaded until the user initiates it. This means the JavaScript play() and load() methods are also inactive until the user initiates playback, unless the play() method is triggered by user action. In other words, a user-initiated Play button works, but an onLoad play event does not.
「携帯が従量課金とかの可能性があるから、autoplayとautobufferは無効にしてるよ。
ユーザが意図して再生を開始するまで、データはロードされないよ。
それは、ユーザのアクションをトリガーにして再生を開始するまでは、JSのplay()とかload()も機能しないってことを意味するよ。
つまり、ユーザによって開始されたplay()は動作するけど、onloadとかのイベントだと動作しないよ。」
って感じでしょうか。
たしかに、実際以下のようなhtmlを書いても、再生されなかった。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>html5 audio test on iPhone</title> </head> <body> <audio src="music.mp3" autoplay></audio> </body> </html>
でも、下のようなhtmlは再生されてはいけないはずなのだが再生された。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>html5</title> <script type="text/javascript"><!-- function onload_play() { var aud = document.getElementById('aud'); aud.load(); aud.play(); } //--></script> </head> <body onload="onload_play();"> <audio id="aud" src="music.mp3"></audio> </body> </html>
これがバグなのかどうかはわからない。
が、いずれにせよ、サイレントモードを無視して再生されるので、
音を鳴らすのは微妙。
なんかいい方法はないのか。。。
(jpmobile の) mobile_filter hankaku => true は controller.response.body が freeze されてるとエラーになる
そもそも response.body が freeze された後に、文字変換すんなよというのは置いておいて。。。
例えば、
class HogeController < ApplicationController mobile_filter :hankaku => true around_filter :my_around_filter # mobile_filter は around_filter なので、filterの実行順は # mobile_filter(before) --> my_around_filter(before) --> action(index) --> # my_around_filter(after) --> mobile_filter(after) # # (mobile_filterは、正確にはオプション指定によるが、around_filter が1〜4つだが、 # ここではまとめて1つとして記述している) def index end private def my_around_filter # before filter では何もしない yield # after filter で、response.body を freeze response.body.freeze end end
これを実行すると、「can't modify frozen string」でエラーになる
エラー箇所は、「vendor/plugins/jpmobile/lib/jpmobile/filter.rb:103:in `gsub!'」
周辺のコードは、次のようになっている。
def filter(str, from, to) str = str.clone from.each_with_index do |int, i| str.gsub!(int, to[i]) # line. 103 end str end
str に response.body が入ってくるのだが、
freezeされているものを gsub! で(全角カナ --> 半角カナ に)破壊的に変更しようとしてエラーとなっている。
ちなみに、hankaku => true 無しだと、問題なく動作する
class HogeController < ApplicationController mobile_filter # 全角・半角変換無し around_filter :my_around_filter def index end private def my_around_filter # before yield # after response.body.freeze end end
これであっても、freeze 後に文字コードを変えているのだが、なぜこっちではエラーにならないのか。
結論から言うと、文字コード変更などでは、response.body そのものを変更しているのではなく、
response.body の文字列をコピーしたものを変更して、それをresponse.body に入れているからだと思われる。
frozen? でresponse.body の状態を出力させてみると
class HogeController < ApplicationController around_filter :my_around_filter1 # 確認用 mobile_filter # 全角・半角変換無し around_filter :my_around_filter2 # 実行順序は、 # my_around_filter1(before) --> mobile_filter(before) --> my_around_filter2(before) --> # action(index) --> # my_around_filter2(after) --> mobile_filter(after) --> my_around_filter1(after) def index end private def my_around_filter1 # before yield # after logger.debug("### #{response.body.frozen?}") end def my_around_filter2 # before yield # after response.body.freeze logger.debug("### #{response.body.frozen?}") end # ログは以下のようになり、mobile_filter で response.body の freeze 状態が解除されているのが確認できる # ### true # ### false end
で、全角・半角変換有りの話しに戻って、気になるのが、エラーが出ていた103行目の2行上にある「str = str.clone」
これは一体何のために存在しているのか。
もし、freezeされていない文字列のコピーを作りたいという考えであるとすれば、
cloneでは意図する動作になっていない。
プログラミング言語 Ruby リファレンスマニュアル の clone, dup の説明に書いてあるとおり、
clone では、freeze状態までコピーしてしまう。
試しに、101行目のcloneをdupに変更したら、問題なく動作した。
これがjpmobileの意図する通りの挙動なのか、バグなのかは分からないが、
全角・半角変換だけがfreezeされた場合にエラーになるのは、個人的には微妙な挙動だと思う。
response.bodyがfreezeされていた時にエラーにするなら、文字コード変換などでもエラーを出力し、
freezeされていてもfreezeを解除して実行してしまうのであれば、
全角・半角変換でもエラーにならないようになっていると、
mobile_filter全体として一貫性がある気がする。
ただ、文字コード変更や、絵文字のキャリア出し分けみたいな変換は、外見を変えるものではないのに対し、
全角・半角変換は外見からして変えるものだから、こういう挙動にしているんだと言われたら、
それはそれで納得できそうな気もする。
before_filter, after_filter, around_filter, prepend_before_filter, prepend_after_filter, prepend_filter の順番整理メモ
before_filter
before_filter :b1, :b2 # b1 --> b2 --> action
before_filter :b1 before_filter :b2 # b1 --> b2 --> action
before_filter + prepend_before_filter
before_filter :b1, :b2 prepend_before_filter :b3 # b3 --> b1 --> b2 --> action
before_filter :b1, :b2 prepend_before_filter :b3 prepend_before_filter :b4 # b4 --> b3 --> b1 --> b2 --> action
before_filter :b1, :b2 prepend_before_filter :b3, :b4 # b3 --> b4 --> b1 --> b2 --> action
別々に prepend_before_filter を宣言したときと、
カンマ区切りで並べたときの順序が同じにならないのは紛らわしいなぁ。。
after_filter
before_filter と同じ
after_filter :a1, :a2 # action --> a1 --> a2
after_filter :a1 after_filter :a2 # action --> a1 --> a2
after_filter + prepend_after_filter
after_filter :a1, :a2 prepend_after_filter :a3 # action --> a3 --> a1 --> a2
after_filter :a1, :a2 prepend_after_filter :a3 prepend_after_filter :a4 # action --> a4 --> a3 --> a1 --> a2
after_filter :a1, :a2 prepend_after_filter :a3, :a4 # action --> a3 --> a4 --> a1 --> a2
around_filter
before_filter, after_filter と同じ
around_filter :ar1, :ar2 # ar1(before) --> ar2(before) --> # action --> # ar2(after) --> ar1(after)
around_filter :ar1 around_filter :ar2 # ar1(before) --> ar2(before) --> # action --> # ar2(after) --> ar1(after)
around_filter + prepend_around_filter
around_filter :ar1, :ar2 prepend_around_filter :ar3 # ar3(before) --> ar1(before) --> ar2(before) --> # action --> # ar2(after) --> ar1(after) --> ar3(after)
around_filter :ar1, :ar2 prepend_around_filter :ar3 prepend_around_filter :ar4 # ar4(before) --> ar3(before) --> ar1(before) --> ar2(before) --> # action --> # ar2(after) --> ar1(after) --> ar3(after) --> ar4(after)
around_filter :ar1, :ar2 prepend_around_filter :ar3, ar4 # ar3(before) --> ar4(before) --> ar1(before) --> ar2(before) --> # action --> # ar2(after) --> ar1(after) --> ar4(after) --> ar3(after)
before_filter + around_filter
before_filter :b1 around_filter :ar1 # b1 --> ar1(before) --> action --> ar1(after)
before_filter :b1 around_filter :ar1 before_filter :b2 # b1 --> ar1(before) --> b2 --> action --> ar1(after)
before_filter :b1 around_filter :ar1 before_filter :b2 around_filter :ar2 # b1 --> ar1(before) --> b2 --> ar2(before) --> # action --> ar2(after) --> ar1(after)
逆
around_filter :ar1 before_filter :b1 # ar1(before) --> b1 --> action --> ar1(after)
around_filter :ar1 before_filter :b1 around_filter :ar2 before_filter :b2 # ar1(before) --> b1 --> ar2(before) --> b2 --> # action --> ar2(after) --> ar1(after)
prepend_before_filter + around_filter
around_filter :ar1 prepend_before_filter :b1 # b1 --> ar1(before) --> action --> ar1(after)
around_filter :ar1 prepend_before_filter :b1 prepend_before_filter :b2 # b2 --> b1 --> ar1(before) --> action --> ar1(after)
around_filter :ar1 prepend_before_filter :b1, :b2 # b1 --> b2 --> ar1(before) --> action --> ar1(after)
around_filter :ar1 prepend_before_filter :b1 around_filter :ar2 prepend_before_filter :b2 # b2 --> b1 --> ar1(before) --> ar2(before) --> # action --> ar2(after) --> ar1(after)
before_filter + prepend_around_filter
before_filter :b1 prepend_around_filter :ar1 # ar1(before) --> b1 --> action --> ar1(after)
before_filter :b1 prepend_around_filter :ar1 prepend_around_filter :ar2 # ar2(before) --> ar1(before) --> b1 --> action --> ar1(after) --> ar2(after)
before_filter :b1 prepend_around_filter :ar1, :ar2 # ar1(before) --> ar2(before) --> b1 --> action --> ar2(after) --> ar1(after)
before_filter :b1 prepend_around_filter :ar1 before_filter :b1 prepend_around_filter :ar2 # ar2(before) --> ar1(before) --> b1 --> b2 --> # action --> ar1(after) --> ar2(after)
prepend_before_filter + prepend_around_filter
prepend_before_filter :b1 prepend_around_filter :ar1 # ar1(before) --> b1 --> action --> ar1(after)
prepend_around_filter :ar1 prepend_before_filter :b1 # b1 --> ar1(before) --> action --> ar1(after)
after_filter + around_filter
after_filter :a1 around_filter :ar1 # ar1(before) --> action --> ar1(after) --> a1
a1 が ar1のafter より後にまわされる。。
around_filter :ar1 after_filter :a1 # ar1(before) --> action --> ar1(after) --> a1
逆は当然か
after_filter :a1 around_filter :ar1 after_filter :a2 # ar1(before) --> action --> ar1(after) --> a1 --> a2
after_filter :a1 around_filter :ar1 after_filter :a2 around_filter :ar2 # ar1(before) --> ar2(before) --> action --> # ar2(after) --> ar1(after) --> a1 --> a2
やっぱ、気持ち悪いなぁ。。
prepend_after_filter + around_filter
prepend_after_filter :a1 around_filter :ar1 # ar1(before) --> action --> ar1(after) --> a1
prepend でも無理。aroundのafterより優先的にはできない。
around_filter :ar1 prepend_after_filter :a1 # ar1(before) --> action --> ar1(after) --> a1
まぁ無理ですよね。
prepend_after_filter :a1 around_filter :ar1 prepend_after_filter :a2 around_filter :ar2 # ar1(before) --> ar2(before) --> action --> # ar2(after) --> ar1(after) --> a2 --> a1
after_filter 同士内でのみ prepend が効いてるが、around_filter には効かない
after_filter + prepend_after_filter
after_filter :a1 prepend_around_filter :ar1 after_filter :a2 prepend_around_filter :ar2 # ar2(before) --> ar1(before) --> action --> # ar1(after) --> ar2(after) --> a1 --> a2
そもそもprependしなくても、after_filterはaroundより優先的には実行されてないから、
当然こうなりますよね。
prepend_after_filter * prepend_around_filter
prepend_after_filter :a1 prepend_around_filter :ar1 prepend_after_filter :a2 prepend_around_filter :ar2 # ar2(before) --> ar1(before) --> action --> # ar1(after) --> ar2(after) --> a2 --> a1
ふむ
まとめ
before と around は自然な挙動だけど、after と around はちょっと気持ち悪い(afterをaroundのafterより先に実行させることができない)
jpmobile のmobile_filter を部分的に無効にする(強引な方法)メモ
使える状況が限られているし、jpmobile自体をいじるし、グローバル変数を汚すしで、
極悪な解だが、一応メモしておく。
たとえば下のように、親コントローラに mobile_filter を記述し、それを継承した子コントローラがいくつかあるとする
# 親 class MobileController < ApplicationController mobile_filter :hankaku => true end # 子 class HogeController < MobileController def index end ... end class FooController < MobileController def index end ... end
で、HogeControllerでは、半角全角変換をしたく無い場合にどうするか。
真っ当な方法としては、mobile_filter の宣言を 親コントローラであるMobileControllerに記述することをやめて、
それぞれのコントローラに記述する方法
# 親 class MobileController < ApplicationController end # 子 class HogeController < MobileController def index end ... end class FooController < MobileController mobile_filter :hankaku => true def index end ... end
たしかにこれでもよいが、さらにHogeControllerの中の
index のみ半角全角変換を外したい場合は、どうすればよいか。
(言い換えると、HogeController でも、index以外では半角全角変換したい場合はどうしたらよいか)
ここからが極悪解だが、
mobile_filter の宣言部分( jpmobile/lib/jpmobile/filter.rb )を以下のようにいじる
class ActionController::Base #:nodoc: def self.mobile_filter(options={}) options = {:emoticon=>true, :hankaku=>false}.update(options) if options[:emoticon] around_filter Jpmobile::Filter::Emoticon::Outer.new end around_filter Jpmobile::Filter::Sjis.new if options[:emoticon] around_filter Jpmobile::Filter::Emoticon::Inner.new end if options[:hankaku] # around_filter Jpmobile::Filter::HankakuKana.new # この1行をコメントアウトし、以下2行を追加 $jpmobile_filter_hankakukana = Jpmobile::Filter::HankakuKana.new # グローバル変数に半角全角変換フィルタのインスタンスを格納して取っておく around_filter $jpmobile_filter_hankakukana end end end
なおこの例では、半角全角変換のみ無効にしようとしているので、1つしかグローバル変数に入れていないが、
全てを無効にしたいのであれば、Jpmobile::Filter::Emoticon::outer, Jpmobile::Filter::Sjis, Jpmobile::Filter::Emoticon::Inner も入れておく必要があると思われる
これで、フィルタのインスタンスが格納されているので、skip_filterやそのオプションが使用可能となった。
つまり、外したいコントローラで、外したいアクションをskip_filterで指定すれば良い
# 親 class MobileController < ApplicationController mobile_filter :hankaku => true end # 子 class HogeController < MobileController # HogeController の index のみ半角全角変換フィルタをskip skip_filter $jpmobile_filter_hankakukana, :only => :index def index end ... end class FooController < MobileController def index end ... end
条件として、グローバル変数を使っているせいで、mobile_filter 宣言は複数回不可能
(どこか1つだけ、正しく動作して、それ以外は効かなくなる)
もっと良い方法があったら教えてくださいorz
ちなみに、rails は 2.3.5 です。
Togetter で「RT」以降の文字を薄くするGreasemonkeyスクリプト
タイトル通りですが、RT/QT という文字列以降を薄く(#999999)するスクリプトを作りました。
下記リンク先のInstallボタンからインストールしてください。
RT obscure for Togetter for Greasemonkey
【2010/07/04 03:08 追記】
Toggeter --> Togetter に修正しました。すみませんorz
JSON.parse に渡せるJSON文字列が厳密すぎて使いにくい件
JSON文字列をオブジェクトに変換してくれる JSON.parse() を使ってみたが、
かなり使いにくく感じた。
というのも、
JSON.parse("{hoge: 1}"); // error
これがSyntaxErrorになる*1
JSON.stringify() が厳密なJSON文字列をくれるらしいので、
どう書けばよかったのか、回答を頂く。
JSON.stringify({hoge: 1}); // {"hoge":1}
試す。
JSON.parse('{"hoge":1}'); // ok
でも、文字列をシングルクオートにすると、
JSON.parse("{'hoge':1}"); // error
SyntaxErrorになる。
値が文字列の場合も同様。
JSON.parse('{"hoge":"foo"}'); // ok JSON.parse('{"hoge":' + "'foo'" + '}'); // error ({"hoge":'foo'} こんな文字列)
うーん。。。
これだとeval使いたくなりますな。。
*1:試した環境は Firefox3.6.3、Chrome5.0.375.70