惨敗に続く惨敗の記録

 暇を見つけては(てか、暇つぶしに) idea:10858『編集画面のバックアップ機能について、Operaでも使えるようにして欲しい。』の解決を考えているんですが、私にはちょっと分かんないです。
 でも、だれか分かる人がいるかもしれませんので、私が試した範囲で、上手くいかない理由(?)を記録しておきます。(続きを読むと、メチャ長いです)

表面上の原

 Operaで編集画面のバックアップ機能が動かないのは、

  • OSはWin,Mac限定
  • ブラウザはIEfirefoxsafari限定(というか、operaのみ明示的に禁止)
  • flashは、事実上flash9以上のみ対応

となっているせいです。
 Operaが外されているのも、linuxが対象外なのも、基本的には「flash9が使えない」という理由によるものだったと思います。

表面上の理由の解決

 そこで、この機能に関係するjavascript「diary_backup.js」と「local_storage.js」のOpera禁止部分を解除すれば、そこそこ動くかと期待しました。

「diary_backup.js」の

if ((ua.indexOf("Win")>=0 || ua.indexOf("Mac")>=0)
    && !window.opera
    && LocalStorage.getFlashVersion()<8) {

と言う部分を、

var isOpera9 = function(){
	if (!window.opera) return false;
	return navigator.userAgent.match(/Opera[\s\/]9\.\d+/);
}

//略

if ((ua.indexOf("Win")>=0 || ua.indexOf("Mac")>=0)
    && !isOpera9()
    && LocalStorage.getFlashVersion()<8) {

と改造し、「local_storage.js」の

if (ua.indexOf("Win")<0 && ua.indexOf("Mac")<0) return false;
if (window.opera) return false;

と言う部分を

if (ua.indexOf("Win")<0 && ua.indexOf("Mac")<0) return false;
if (window.opera) return navigator.userAgent.match(/Opera[\s\/]9\.\d+/) 

と改造してみました。
 これで、(少なくてもWin上の)Opera9でも、ダイアリー編集画面に「バックアップ機能」を“表示”できるようになりました。

本質的な問題

 これでサクサクとバックアップ機能が動くようになるかと思ったら、世の中そんなに甘くなく、バックアップを取りもしなければ復元ウィンドウもマトモに開きません。容量変更なんてテストすら怖い状態です。
 これを何とかしないといけません。

本質的な問題の調査

 エラーコンソールから監視していますと、

  • message: Statement on line 104: Type mismatch (usually a non-object value used where an object is required)
  • Line 104 of linked script local_storage.js
    • return this.flash.getKeys(objName);
  • Line 75 of linked script diary_backup.js
    • var keys = this.storage.getKeys(this.objName);
  • Line 49 of linked script prototype-1.4.0.js
    • return __method.apply(object, args.concat($A(arguments)));
  • Line 113 of linked script prototype-1.4.0.js
    • this.callback();
  • Line 49 of linked script prototype-1.4.0.js
    • return __method.apply(object, args.concat($A(arguments)));
  • At unknown location
    • [statement source code not available]

と表示されます。(見やすくなるよう、編集してます)
 どうやら、「local_storage.js」で「this.flash.getKeys」か「objName」が見つからないようです。


 同スクリプトの当該箇所をalertで確認したところ、「objName」は「hatena-diary-edit」、「this.flash」は「 [object HTMLEmbedElement] 」と表示されました。
 「objName」の「hatena-diary-edit」は、編集画面のメインの編集領域のことで、これは有ってます。疑うべきは、「this.flash」の「 [object HTMLEmbedElement] 」と判断しました。


 そこで、alertを使って、「this.flash」の「getKeyCount()」を調べようとしましたが、やはり「Type mismatch」になります。
 これを如何に解決するかが問題です。

可能性1

 ひょっとすると、OperaFlashの相性の問題かもしれません。だったら私はお手上げです。

可能性2

 あるいは、「local_storage.js」でのIEの判定が甘く、OperaでのフラッシュプラグインはEMBED要素になってるのにバージョンチェックはActiveX側で行ってるようなチグハグさが原因かも分かりません。

可能性3

 更には、「local_storage.js」のgetMovieメソッド

    getMovie: function(flashId) {
        if (navigator.appName.indexOf("Microsoft") != -1) { // IE
            return window[flashId];
        } else {
            return document[flashId];
        }
    }

の、『window[flashId]』とか『document[flashId]』みたいな、私には謎の文法に秘密が隠されているのかも分かりません。


 おおよそ、この辺に原因がありそうです。他にも疑わしいところがあるのかもしれませんが。。。
 疑わしいところは片っ端からチェックしてきたのですが、「もう疲れたよパトラッシュ」状態です。
 以下に、疑うだけ疑って、その結果シロだったものを掲載しておきます。

勘違い1 「prototype-1.4.0.js」でのbind

 単なる直感だったのですが、「diary_backup.js」から使用している「prototype-1.4.0.js」の段階で、「extend(this)」に失敗している予感がしました。


 調べてみると、このコールバック関数の源は、「diary_backup.js」の「initialize(初期化)」の際に、「prototype-1.4.0.js」の組み込みクラスである「PeriodicalExecuter(タイマークラス)」*1を用いて、自分自身(DiaryBackupクラス)のメソッドである「onPeriodicalSave」メソッドをスケジューリングしています。もちろんここで、自分自身(DiaryBackupクラス)をbindしています。ここまでは正常。
 で、スケジューラ「prototype-1.4.0.jsのPeriodicalExecuter」から呼び出された「onPeriodicalSave」メソッド(thisは自分自身(DiaryBackup)のクラス)内で、「var keys = this.storage.getKeys(this.objName)」を発行しています。これが上記エラーコンソールの背景です。(このルートを調べるのが面倒で放置してました。ごめんなさい)


 ココで呼び出されたstorageクラス*2で、「this.flash」が見つからないのが問題のようです。


 じつわ、この段階で、「LocalStorage」クラスのthisが「DiaryBackup」クラスになっちゃってるのでわ!!


 と思ったのですが、Operaでも、「this」は「LocalStorage」クラスのインスタンスになっているようです。
 thisの正体は、各クラスに(クラス毎に違うアタイを返す)getSasadaName()関数を仕込んでalert表示させる実験で確認しました。
 「LocalStorage」クラスでthis.getSasadaName()関数を使用すれば、ちゃんと「LocalStorage」と返してきます。もちろん「DiaryBackup」クラスでthis.getSasadaName()関数を使用すれば「DiaryBackup」と返してきます。ついでに、LocalStorage.getSasadaName()関数(親メソッド)もちゃんと「Ancestor of LocalStorage」と返してきました。
 このセンは消えました!!

勘違い2

 ネットで検索して知ったのですが、「prototype-1.4.0.js」のevalScripts()はOperaでは動かないとのことで、当スクリプト

return this.extractScripts().map(eval)

return this.extractScripts().map(function(a){return eval(a)});

に書き換えてみました!!
 ・・・全然、変わりませんでした。orz


 結局ぐるっと一周廻って来て、「thisは正常、でもthis.flahは怪しげ」という状態です。
 さて、この状態を、どうやって解決したものでしょうか。。。
 だれか、ヘルプ・ミー!!

参考

 こういうのって、Opera単独の問題かと思ったら、Firefoxで同じエラーメッセージに悩まされた方もいらっしゃるようです

Firebugのエラーコンソールをみたら

GET http://ottava.jp/live/frame_flag.txt?clock=1176818136093 (63ms)

とGETしに行った後に

this.flash.getKeys is not a function
[Break on this error] return this.flash.getKeys(objName);

といったエラーが出てました。this.flash.getKeysがないようです。
なんか読み込まれてないんだろうかなぁ・・・

OTTAVAがFirefox上で再生できないので、それっぽそうなプラグインを入れてみたわけですがだめでした。 - Mattari Diary

 なんか、ビミョーです。。。

*1:PeriodicalExecuterについてはhawklab.jpにソースと説明あり。

*2:storageクラスは、「diary_backup.js」の「initialize(初期化)」の際に、「local_storage.js」の「LocalStorage」クラスから生成されています。