シリーズの他の記事:
ご注意
これらの例はさらに慎重に簡素化されており、グローバルな名前空間が非常に乱雑になり、セキュリティについては気にしません。 これらの問題は個別に扱われます。
コードの再利用:スクリプトの読み込みを効率化
Operaバージョン10までのuserjsのロード順序は、オペレーティングシステムがファイル名を表示する順序によって決まります。 一般に未定義。 OS、FSドライバー、FS自体、および火星の天気に依存します。 10番目のバージョンから、Operaはアルファベット順にファイルをアップロードします。すでに簡単ですが、それでも十分に便利ではありません。
起動順序が保証されていないため、通常のコードの再利用が妨げられます。 もちろん、関数がイベントハンドラーでのみ必要な場合(この時点ではすべてのスクリプトが既に読み込まれている場合)、これは問題ではありませんが、userjs自体を読み込むときにもしばしば必要です。 そして、この場合、各スクリプトは必要な機能をすべて引き出して、車輪を再発明しなければなりません。
関数をすぐに実行する代わりに、関数を配列に追加します。
if(!(ウィンドウ内の「スクリプト」))scripts = []; //スクリプトが最初にロードされる場合、配列が存在しない場合があります。 scripts.push(関数(){/ * bla-bla-bla * /});
コードの起動には、特別なスクリプトloader.jsが関与します。
if(!(ウィンドウ内の「スクリプト」))scripts = []; for(var i = 0; i <scripts.length; ++ i)スクリプト[i](); スクリプト= {push:function(f){f(); }};
最後の行に注意してください: loader.jsの後に読み込まれたスクリプトは、新しいプッシュ関数を呼び出します。これは、関数を配列に入れず、すぐに実行します。
これまでのところ、変更によって何も得られませんでした-関数は依然としてランダムな順序で配列に配置されています。 ただし、関数の配列全体が手元にあるので、この順序を制御できます。
たとえば、依存関係のリストを使用できます。
scripts.push({ 名前: 'example'、 必須:['utils'、 'xpath']、 init:function(){/ * bla-bla-bla * /} });
スクリプトをロードするときに、このスクリプトが最後にロードされたことを確認する方法がないことに注意してください。 すべてのスクリプトはすでにDOMContentLoadedなどのシグナルハンドラーでロードされていると言えますが、Operaがユーザースクリプトに提供する高度な機能( opera.addEventListener 、 defineMagicVariableなど)はハンドラーで使用できなくなりました。 したがって、新しい関数が追加されるたびに依存関係の分析をすぐに実行し、すべての依存関係が満たされるとすぐに関数を実行する必要があります。 また、ページロードイベントハンドラーでは、依存関係が満たされていない関数を既に特定できます。
この意味で、10番目のバージョンのリリースと配布は、コードを簡素化するのに役立ちます-最後にロードされることが保証され、依存関係の計算とすべての機能の起動を実行する、辞書式姓のスクリプトを追加できます。
スクリプト間でデータを交換する
最初の記事で述べたように、グローバル名前空間を使用しないでください。 しかし、あるスクリプトのオブジェクトを別のスクリプトからどのように使用するのですか(そして、なぜ他のスクリプトがロードされる順序を保証する必要があるのですか)?
最初の方法は、別の名前空間を使用して、ページ上のスクリプトとの競合を単純に回避することです。
unique_script_name.js
unique_script_name = { doIt:関数(){...}、 dontDoIt:function(){...}、 };
some_other_script.js
unique_script_name.doIt();
2番目の方法では、変更されたloader.jsを使用しますが、データはページのスクリプトからは完全にアクセスできません。
loader.js :
(関数(){ var shared = {}; if(!(ウィンドウ内の「スクリプト」))scripts = []; for(var i = 0; i <scripts.length; ++ i)スクリプト[i](共有); scripts = {push:function(f){f(shared); }}; })();
これで、各スクリプトはinit関数の引数として共有オブジェクトを受け取ります。 このオブジェクトは、 ウィンドウオブジェクトからは到達できません(もちろん、一部のuserjsがそれをバカにしていない限り)。
if(!(ウィンドウ内の「スクリプト」))scripts = []; scripts.push(関数(共有){ shared.very_useful_script = { doIt:関数(){...}、 }; });
スクリプト構成
多くのuserjsには事前設定が必要です。 通常、セットアップはスクリプトを開き、いくつかの変数の値を編集します。 しかし、このための通常のユーザーインターフェイスを提供したいと思います。 ブラウザーでインターフェイスを作成することは問題ではありませんが、できれば追加費用なしでスクリプトが取得できるように構成を保存する場所はどこですか?
設定がページに対してローカルである場合、すべてが簡単です。設定をCookieに保存します。 ただし、グローバル設定ではこれは適切ではありません。
1つのオプションは、以下で説明するトリックを使用して構成をグローバルリポジトリまたはファイルに保存することですが、追加のフレーム、プラグイン、または各ページのJavaのロードが必要です。 より安価なオプションがあります-設定をuserjsとして保存します。 これは、LiveConnectまたはJavaアプレット(両方のオプションでJavaが必要)を使用するか、ユーザーにデータを保存するように依頼するだけで行えます。//-スクリプトフォルダへのリンク。
構成スクリプトの例:
if(!(ウィンドウ内の「スクリプト」))scripts = []; scripts.push(関数(共有){ shared.configuration = { example_script:{timeout:10; }、 another_script:{password: '123'}、 }; });
構成は、トリックを使用せずに、Opera自体に高速でロードされます。 loader.jsを使用すると、残りのスクリプトが実行される前に構成がロードされることを保証できます。
XDM-クロスドメインメッセージング
XDMは、異なるドメインからのドキュメント間で情報を交換する方法です。 2つのコンポーネントで構成されます。
- 別のウィンドウに文字列を送信するWindow.postMessage(str)関数。 フレームにメッセージを送信するには、 iframe.contentWindow.postMessage(str)を使用します
- メッセージを受信したときに発生するメッセージイベント。 イベントオブジェクトのデータフィールドにはメッセージ文字列が含まれ、ソースフィールドには送信者ウィンドウへのリンクが含まれます。
window.addEventListener( 'message'、function(ev){ アラート(「メッセージを取得:」+ ev.data); ev.source.postMessage( 'got it'); }、true); iframe.contentWindow.postMessage( 'test');
注意! XDMの単純な使用は危険です。 ページ上のスクリプトはiframeリンクを取得してメッセージを送信できるため、共有ストレージから重要な情報を受信したり、クロスドメインリクエストを実行したりできます。 保護方法については、次の記事で説明します。
共有データウェアハウス
GreaseMonkeyスクリプトと比較して、Opera userjsには便利な関数GM_getValue 、 GM_setValue 、 GM_deleteValueがなく 、すべてのページの共通ストレージにデータを保存できます。 ただし、次の事実を利用して、それらの機能をエミュレートできます。
- userjsは、エラーのためにロードされなかったページでも実行されます。
- Operaはフレーム間のメッセージ転送をサポートしています(XDM-クロスドメインメッセージング)。
- アドレス「 0.0.0.0 」のページをロードすることはできません。
このトリックは「Opera 0.0.0.0 hack」と呼ばれますが、実際には他のアドレスでもかまいません。 正しいアドレスを使用するのは安全ではありません。 そのため、スクリプトは2つの部分で構成され、1つはアドレス「 0.0.0.0 」のページで実行され、もう1つは残りのすべてで実行されます。 最初の部分はXDMメッセージをサブスクライブし、2番目はアドレス「 0.0.0.0 」で非表示のiframeをページに作成し、コマンド(get / set / delete)でメッセージを送信し、結果を受け取ります。 データ自体は、「 0.0.0.0 」ページのCookieに保存されます。
if(location.href == 'http://0.0.0.0/'){ document.addEventListener( 'message'、function(evt){ var a = msg.data.split( '|'); switch(a [0]){ case 'get':evt.source.postMessage(getCookie(a [1])); ケース 'set':setCookie(a [1]); case 'del':delCookie(a [1]); } }、true); } else { document.addEventListener( 'message'、function(evt){ alert( '値を取得:' + evt.data); }); document.addEventListener( 'DOMContentLoaded'、function(){ var iframe = document.createElement( 'iframe'); iframe.style.display = 'none'; iframe.onload = function(){ iframe.contentWindow.postMessage( 'set | name | value'); iframe.contentWindow.postMessage( 'get | name'); } iframe.src = 'http://0.0.0.0/'; document.documentElement.appendChild(iframe); }、true); }
クロスドメインXMLHttpRequest
XMLHttpRequestが他のドメインへのリクエストを許可されている場合、多くの便利なスクリプトを作成できます。 これは、スペルチェック、翻訳、自動補完などです。 しかし、ここではOperaはユーザーをまったく同情しませんでした。userjsにはページ上のスクリプトと同じ「同じ起源」の制限があります。
ただし、共有ストレージと同じXDMを使用すると、別のドメインのコンテキストでXMLHttpRequestを実行できます。 例を挙げません。前の例と似ていますが、 getCookieを呼び出す代わりにXMLHttpRequest呼び出しがあります。
トリック フレーム内のドメインのコンテンツを無駄にしないために、「domain.ru」の代わりに「-xmlhttprequest.domain.ru」を読み込むことができます。ドメイン名は特に誤っているため、ドメインが誤ってそのようなサブドメインを持つことさえできません。 次に、フレームのコンテキストで実行する必要があります
document.domain = "domain.ru";
その後、 XMLHttpRequestを使用できます。
Liveconnect
LiveConnectは、JavaSciptからJavaコードを呼び出す方法です。 Netscapeに最初に登場し、Operaで動作します。 グローバルjavaオブジェクトは、Javaパッケージおよびクラスへのアクセスを提供します。
コード例:
//ファイルが存在するかどうかを調べます。 var file_exists =(新しいjava.io.File(パス))。exists();
Javaを使用すると、ファイルへのアクセス(書き込みを含む)、クリップボードの操作、ソケットの使用など、多くのことができます。 詳細については、Javaのドキュメントを参照してください。
しかし、デフォルトでは、もちろんこれはすべて禁止されています。そうしないと、信頼できないサイトへの最初のアクセスが破損してしまいます。
許可はJavaポリシーファイルに書き込まれます。 「opera:config#Java | SecurityPolicy」設定からパスを見つけることができます。 既存のファイルを変更せずに、コピーして設定に登録することをお勧めします。
ポリシーファイルの公式ドキュメントは、SunのWebサイトにあります。
許可エントリの例:
codeBase "http://some.host.ru/"を許可する{ //すべてを許可します。 //パーミッションjava.security.AllPermission; //フォルダ「〜/ .opera / userjs / data」内のファイルへのフルアクセスを許可します。 // $ {/}はシステムパスの区切り文字を置き換えます:Unixでは「/」、Windowsでは「\」。 //パスの末尾の「-」は、「再帰的に内部のすべてのフォルダーとファイル」を意味します。 //パーミッションjava.io.FilePermission "$ {user.home} $ {/}。opera $ {/} userjs $ {/} data /-"、 "読み取り、書き込み、削除"; //クリップボードへのアクセスを許可します。 //パーミッションjava.awt.AWTPermission "accessClipboard"; };
注意! 任意のパスに権限を設定すると、userjsスクリプトだけでなく、ページからのスクリプトに対しても許可されたアクションを実行できます。 これを望んで誰もが自分のサイトでLiveConnectを使用することはまずありませんが、セキュリティの点で妄想的である方が良いです。 この問題を解決する方法は、次の記事で説明します。