ちょっと2年ぐらい前の話になるのだが、
はてなブログの機能に
んで、記事で紹介した。
www.citrussin.com
そしたら、意外と反響を頂くことになって。
ありがたいことに大勢の人に見てもらえて、更には何人かの人には実際に使ってもらっているみたい(゚∀゚)
これは嬉しいことやねと思う反面前回やり残したことがいくつかあるなぁ・・・と思ったので。
今回はあのブックマークレットに、傍点削除機能を付け加えることにする。
経緯というか、やり残しのはなし。
前回の記事では最後にこのようなことを言っているんですよ。
使用上の注意
- 既に作成したブログ記事上の左上に出てくる編集ボタンで、簡易編集画面(ポップアップ編集画面)を出した場合は使用不可
- 面倒なので削除機能は作ってない。一方通行なので「間違って付けちゃった!」ってときは手動修正。
がしかし、この手製レットを二年間使い続けているとこの仕様に結構イライラする。
んーまぁ、正直実装当時は「ここまででええやろ」的な、めんどくさいし的なアトモスフィアがあったんよ。
でもなぁ、ここまで人が見てるとなぁ・・・。
めんどくさいなんて言ってられない。
どうにかこうにか「ブックマークレット削除機能」をつけ加えてみよう。
ついでに「簡易編集画面操作」の挙動も確認してみることにする。
処理
注:単に使いたいだけの人は、「ただ使いたいだけのひと」まで読み飛ばしてください。
要求定義
前提
- 前回の要求はすべて守る
- 機能の削除や最適化は今回の要件に入っていない。
- 前回のブックマークレットに何行か足して機能を拡張する。
- 新しく作り直すのは面倒
- できれば作成は小規模で終わること
- ちょっと付け加えるだけ。複雑なことはしない
- 削除機能は正規表現で可能なため、問題はおそらく簡易編集画面の方
要求
- 私が前回作ったブックマークレットで付与した傍点のruby部を削除する
- 他人が付加したrubyは無視して良い(スペースのありなし判定はしない)
- 同じブックマークレットボタンで、傍点の付与と削除が行えること
- ボタンが増えてもうっとおしい。rubyにrubyすることはないやろたぶん(;・∀・)
調査
- 簡易編集画面(その場で編集機能というらしい)でもruby追加ブックマークレットが使えるようにできるのかを確認。
ブックマークレットの準備及び開発準備
前回参照。
文字への”ルビふり機能”がはてなブログ編集画面で対応してないから、簡単に使える”傍点””ルビ振り”用ブックマークレット作ったよ。使い方と説明まとめ。 - citrussinのチラシの裏
ruby部削除機能
方針
さて、citrussin手製の傍点レットを使った場合、
[文]
が
<ruby><rb>[文]</rb><rp>(</rp><rt>・・・</rt><rp>)</rp></ruby>
となります。
ということは、選択した文章の始まりが<ruby><rb>で、終わりが</ruby>ならばルビを削除。
<ruby><rb>でないならばルビを付与すればいいということ。
なので、matchを使って正規表現で引っこ抜き、値がnullかどうかで条件分岐してやればOK。
正規表現は、傍点レットの場合スペースが入らずタグの形が決まっているので、
/^<ruby><rb>(.*)<\/rb>.*<\/ruby>/;
とでもしましょうか。
あとは、
値が入っていれば、正規表現で引っこ抜いた<rb>[文]</rb>の[文]だけ取り出してやる。
nullならば前回のシーケンスに移行する。
とすれば、同じボタンで付与と削除が可能です。
ね? 簡単でしょ?
正直30分もいらんかった(;・∀・)
かいたー
(実際に使うときはコメントを削除すること)
javascript:(function(){ // id:bodyを探してelementゲット var d=document.getElementById( "body" ); // 選択したカーソルのはじめと終わりの場所 var ss = d.selectionStart; var se = d.selectionEnd; //選択中の文字列 var sub = d.value.substr(ss,se-ss); // 選択された文字列より前にかかれている文字列 var pre = d.value.substr(0,ss); // 選択された文字列より後にかかれている文字列 var post = d.value.substr(se,d.value.length); //ここから追加 //rubyをくり抜くための正規表現 var pat = /^<ruby><rb>(.*)<\/rb>.*<\/ruby>/; // 選択中の文字を正規表現にパターンマッチ var patres = sub.match(pat); if(patres){ //結果がyesならばくり抜いた本文だけsubにいれる sub = patres[1]; }else{ //結果がnoならば前回同様<ruby><rb>傍点</rb><rp>(</rp><rt>・・</rt><rp>)</rp></ruby>をつける //rubyタグはここでつける。 var dot = "・".repeat(sub.length); sub = "<ruby><rb>" +sub +"</rb><rp>(</rp><rt>"+dot+"</rt><rp>)</rp></ruby>"; } //pre,sub,postを結合して文字列を再生成 var str = pre +sub +post; d.value = str; })()
このまま使う場合はブックマークレット処理する
javascript:(function(){var%20d=document.getElementById(%20%22body%22%20);var%20ss%20=%20d.selectionStart;var%20se%20=%20d.selectionEnd;var%20sub%20=%20d.value.substr(ss,se-ss);var%20pre%20=%20d.value.substr(0,ss);var%20post%20=%20d.value.substr(se,d.value.length);var%20pat%20=%20/^<ruby><rb>(.*)<\/rb>.*<\/ruby>/;var%20patres%20=%20sub.match(pat);if(patres){sub%20=%20patres[1];}else{var%20dot%20=%20%22・%22.repeat(sub.length);sub%20=%20 %22<ruby><rb>%22%20%20%20%20%20+sub%20%20%20%20%20+%22</rb><rp>(</rp><rt>%22+dot+%22</rt><rp>)</rp></ruby>%22;}var%20str%20=%20%20%20%20%20%20pre%20%20%20%20%20+sub%20%20%20%20%20+post;d.value%20=%20str;})()
終了。
深く考えず、ブックマークレットを使いたい人向け、使い方説明
手製傍点ブックマークレットの使い方
前回と同じ運用です。
前回のブックマークレットは削除してください。
前回と同じように、ブックマークレットを使用すると
- 傍点をつけたい文章を選んで
- ブックマークレットを押すと傍点がつく
そして、ルビタグを含めた傍点を選択してブックマークレットを使用すると消えます。
- 消したいルビタグ付き文章を選んで
- ブックマークレットを押すと消える
ルビ振り用
削除機能の正規表現は、ルビの欄が「.*」(すべての文字列にマッチ)になっているので、ルビも普通に削除できる。
前回同様、dot部をpromptに変えてやろう。
めんどくさい方は前回同様こちらを
ルビ振り用リンク
ブックマークバーまでD&Dすればいいと思う。
二年間の間に傍点書き換え機能を使うことが全くなかったので、あの機能は割愛。
欲しい人は前回同様promptを加えてやればいい。
簡易編集画面での使用可能処理がやりたかったんだが。
方針
まぁ、削除機能など正規表現噛ませばすぐできる。
本題に入ろう。
拙作のブックマークレットをここまで多くの方に読んでいただいているのだ。
ぜひとも簡易編集画面でも使用可能にしたいなぁと思う今日このごろ。
しかし、肝心の「簡易編集で使うtextarea」はどこにあるのだろう?
textareaをたずねて三千里。
開発者ツールを起動してチクチク探してみたところ、
「div id="hatena-diary-edit-in-place”」内にあるiframe(idなし)の、そのまた中にあるdocument内にあるtextareaさんがその人だった。
ここが私のアルゼンチンか(゚∀゚!)
だがiframe内にあるため、そのままdocumentからは取りにいけない。
さてどうやって見に行くべきか(;・∀・)
いろいろ試してみたが、結果だけ言うとブックマークレットでjQueryを使うのはめんどくさすぎるので、DOMで操作することになる。
id="hatena-diary-edit-in-place”のdivからiframeを取得して、そのiframeにcontentWindow.document(旧式)なりcontentDocument(IE8以降)を当て込む。
そうればiframe内のdocumentが得られるので、取得したdocumentから改めてgetElementById( "body" )に持ち込むこととしよう。
ね?かんたんでしょ?
こうして母は助かり、マルコは父の待つジェノバへと帰るわけだ。
どっとはらいーーーーー
といけば苦労しない。とりあえずやってみよう。
立ちふさがる「Permission denied」
iframeにアクセスする方針はついた。
とりあえず、本当にそれでアクセスができるのかを確認してみよう。
javascript:(function(){ //「id="hatena-diary-edit-in-place”」内にあるiframeを取り出す。 var edform = document.querySelector("#hatena-diary-edit-in-place > iframe"); // iframeからdocumentを取り出す。 var d = edform.contentWindow.document; // 取り出した中身を見てみる。 console.log(d); })()
結果
SecurityError: Permission denied to access property "document" on cross-origin object (function(){var edform = document.querySelector(":1
・・・・・・・あ、はい。
いやいや、やはりIE8以降なのだからcontentDocumentとすべきだろう。
うん、そうに違いない。
var d = edform.contentWindow.document;
を
var d = edform.contentDocument;
として再実行
結果
(dの中身が)
null
ちょっとググってみよう。
「firefox , contentDocument , null 」で検索だフィリップ
=>クロスオリジン contentDocument が null を返します | Firefox サイト互換性情報
クロスオリジン contentDocument が null を返します
フレーム上の contentDocument プロパティが、スクリプトの呼び出し元にそのドキュメントが含まれない場合、null を返すようになりました。
・・・・あ、はい。
おそらくhatenaのedit-in-place機能は何らかのプラグインで構成されていることだろう。たぶん。
で、そこに触るには権限がない(;・∀・)
どうすんだよ・・・。
今後の話
ブックマークレットでのjavascriptよくわかんね・・・・・。
window.selectionも使えなかったし。
とか思ってたら、こんなバグレポート見つけた。
openradar.appspot.com
10年前のレポートだけど、ブックマークレットではwindow.selectionが使えないらしい。
さて、結局CORS違反になるからedit in place時の文章に触れないのか、それとも私の取得経路が間違っているのか。
その切り分けもできていない気がする。
もしCORS違反ならば回避方法が思いつかないし、取得経路は・・・・普通にdocument => div => iframeって子ノードたどってるだけだしなぁ。
ここが乗り越えられたら普通に簡易編集画面でも使えるんですけどねー(´・ω・`)
とりあえず今回は削除機能を追加したということで。