ライブラリの読み込みをしたいのだけど・・・

いくつもスクリプトを書いていると何回も登場してくる処理っていうのが出てくる。もちろん、毎回functionをコピペしてもいいのだけど、関数が多くなってくると僕的にはソースが読みづらくてあまり好きじゃない。*1
ちなみにここで言ってる”ライブラリ”は自作のしょぼい関数のことです。

そこでSQLiteを使ってライブラリをDB化してスクリプトを書くときに呼び出して使いたい。

DBに入れておけばその呼び出し用の関数だけコピペすればあと数行書き足すだけでいいのですっきりしたスクリプトが書けるかなと。
DB化のメリット

  1. ソースが割とすっきりする。
  2. 書いているときの手間を省ける。

デメリット

  1. 呼び出す関数が多くなると処理速度が遅くなるかも。
  2. 人に使ってもらう場合、DB(ライブラリ)も別途配布する必要がある。

でも、とりあえずやってみる。

イメージ*2

require("p");      //pという名前の関数を読み込む。
p("Hello!World");   //Hello!Worldとアラートが表示される。

DBには

function p(arg) {
	sleipnir.alert(arg);
}

という関数を"p"という名前で登録しています。
ここに”Library.db”というファイル名でおいています。
そこで

function require(name) {
	var db = sleipnir.Database.Open("Library"); 
	var res = db.Query("SELECT * FROM lib;");
	
	if (res.NumRows() == 0) { 
		sleipnir.alert("DBがありません");
	}else{
		res = db.Query("SELECT name, lib FROM lib;");
		var row = res.FetchArray(); 
		if(row.At(0) == name){ 
			return eval(row.At(1));
		}else{
			sleipnir.Status = "みつかりません。";
		}	
	}
} // End of require

require("p");
p("Hello!World");

としても”オブジェクトを指定してください。”とエラーが出る。
require関数でeval()したものが関数の外に出てないみたい。
妥協してこうすると

function require(name) {
	var db = sleipnir.Database.Open("Library"); 
	var res = db.Query("SELECT * FROM lib;");
	
	if (res.NumRows() == 0) { 
		sleipnir.alert("DBがありません");
	}else{
		res = db.Query("SELECT name, lib FROM lib;");
		var row = res.FetchArray(); 
		if(row.At(0) == name){ 
			return row.At(1);
		}else{
			sleipnir.Status = "みつかりません。";
		}	
	}
} // End of require

eval(require("p"));
//require("p");
p("Hello!World");

ちゃんとアラートが出てくる。ダサイ。

function()内でevalした値を外で使いたいのだけどどうすればいいんだぁぁぁ

2007/07/26 追記

Makotoさんのコメントにて

『関数を下のようにしてもダメかの? p=function(arg){sleipnir.alert(arg);}』

とアドバイスをいただきました。ありがとうございました。

試してみると見事に理想的な動作が得られました。
ほんと奥深い〜。

こちらで勉強します。

*1:僕の書き方が汚いだけって知っている

*2:Ruby風味に

TextMateでSleipnirのスクリプトが使える〜(無理矢理)

ヱビスブログ » TextMateでEUCなファイルを無理矢理扱う
こちらのブログでEUCで開く&保存の方法が紹介されています。
ということで僕はShift-JIS版

#!/usr/bin/env ruby
#Save as Shift-JIS
require "nkf"
content = NKF.nkf('-Ws -m0 -x',STDIN.read)
open(ENV['TM_FILEPATH'],'w'){|io| io.write content}
#!/usr/bin/env ruby
#Open Shift-JIS
require "nkf"
print NKF.nkf('-Sw -m0 -x',open(ENV['TM_FILEPATH']).read)

単にeucの"e"をShift-JISの"s"にそれぞれ替えただけです(汗)
Shift-JISが扱えるといっても、実際には見ての通りファイルを開くときにはShift-JIS→UTF-8エンコーディング。保存の時もその逆をしているだけです。
とはいってもこれはすばらしい。今まではSleipnirのスクリプトをTextMateで書きたくてもエンコーディングの問題でイマイチ使えなかったのですから。

実はこういう風にrubyでこの手の拡張スクリプト(のようなもの)を書けるってことしりませんでした。TextMateって素敵。これで日本語の表示さえ正式に対応してくれたら完璧かも。

つくるぶでの外部サイトへのリンクをダイレクトにするスクリプト

つくるぶ -デベロッパー応援プロジェクト
ここの
つくるぶ -デベロッパー応援プロジェクト
とかだと外部サイトへのリンクをクリックすると

となって個人的にうざかったので作ってみました。

たぶんちゃんと動きます。
妙なサイト(セキュリティ上まずいとこ)へのリンクはおそらく”つくるぶ”内にはないと思いますので大丈夫でしょう。

// ==UserScript==
// @name つくるぶ外部サイトへ直行
// @description つくるぶで「この先は外部のページとなります。よろしいですか?」を回避
// @author      stewie   http://d.hatena.ne.jp/griffin-stewie/
// @include        http://www.tkrb.jp/*
// ==/UserScript==

var links = document.getElementsByTagName("A");
var len = links.length;
for(i=0;i<len;i++){
	if(links[i].href.match(/http:\/\/www.tkrb.jp\/jump\.php\?url=(.*)/i)){
		links[i].href = RegExp.$1;
	}
}

追記

UTF-8で保存したらFirefoxGreaseMonkey)でもOKみたいです。

セキュリティ

こちらでプヨぷよ氏が書かれています。

IHTMLWindow2に渡したオブジェクトはページ内のスクリプトからもアクセスできるので、ISleipnirを渡してしまうとmixiで漏らしたように割と洒落にならないセキュリティホールが出来てしまう。

ローカルで試してみました。

ISleipnirを渡すだけのSeaHorseスクリプトを作成。
ボタンをクリックするとこうなりました。

確かにやばいです。(汗)
プヨぷよ氏が書かれているようにこんなJavaScriptが仕込まれているサイトはない(多くもならない?)だろうとは思いますが、少なくてもISleipnirを渡すSeaHorseスクリプトを不特定多数のサイトで使うのはだめですね。いたずらでいきなり全タブを閉じるとかされたりするかも。その程度で済めばかわいいものですが。信用できそうなサイト以外での使用はダメです。

変数にアクセス

当然ながらサイト側のグローバルな変数があるとSeaHorse側で弄れます。また逆に、SeaHorse側の意図しない動作になるともいえますね。
このあたりはクロージャを使えば回避できるとは思います。
僕はそのあたりのことがイマイチ勉強不足です。

うろ覚えですが

FireFoxGreaseMonkeyでも似たような問題があったような気がします。
それでunsafeWindowができたように記憶しています。
SeaHorseプラグイン自体にもそういった仕組みが必要なのかもしれません。

今のところ

スクリプトを書く側が対策をするしかないでしょう。
僕にはそこまでの技量があるとは思えないので自作スクリプトを晒すのは控えた方がいいかなぁ。

折角、今日(仕事用ですが)非同期XHRができたのに(涙)

SleipnirのSeaHorseでドメイン越え

似非GM_XHRかも。

  • ドメイン越え
  • Sleipnirがフリーズしない。
  • WSHではない。

SeaHorseスクリプトができたかも。

ほとんどプヨぷよ氏のパクリです(__;)

実用性0だけどとりあえずあげておきます。
単にgooのトップページのケツにはてなのトップをつないだだけです。

// ==UserScript==
// @name SH_XHR
// @description えせGM_xmlHTTPRequest
// @include http://www.goo.ne.jp/
// @type SleipnirScript
// ==/UserScript==


/*******************************************
動作説明
	ページ内のドキュメントにSleipnirが作成したオブジェクトを持たせること(?)でブラウザのフリーズを回避。
	たぶんこれで非同期化ができてると思う。
	onloadイベント(読み込み時)でSH_XHRが着火。
	onloadイベントは一回しか呼ばれないので非同期モード(引数=true)が可能。
	SH_XHRはonunloadイベントで消される。
********************************************/


if (_document) {
	Init(_window);
}

//SleipnirScript
function Init(targetWindow)
{
	var frames = targetWindow.document.getElementsByTagName("frame");
	if (frames.length) {
		for (i = 0; i < frames.length; i++) {
		 Init(frames[i].contentWindow);
		}
	}
	else if (!targetWindow.dict) {
		targetWindow.xmlhttp = sleipnir.CreateObject("MSXML2.ServerXMLHTTP");
		
		jscript = targetWindow.document.createElement("script");
		jscript.type = "text/javascript";
		jscript.text = SH_XHR + closeSH_XHR;
		targetWindow.document.getElementsByTagName("head").item(0).appendChild(jscript);
		
		targetWindow.attachEvent("onload", targetWindow.SH_XHR);
		targetWindow.attachEvent("onunload", targetWindow.closeSH_XHR);
	}
}

//JavaScript
function SH_XHR()
{
	window.xmlhttp.onreadystatechange = function() { 
	 	if (xmlhttp.readyState == 4) { 
	    	var v = xmlhttp.responseText; 
	    	document.body.innerHTML += v;
	 	} 
	}; 
	xmlhttp.open("GET", "http://hatena.ne.jp/",true, "", "");
	xmlhttp.send(null);
}

//JavaScript
function closeSH_XHR()
{
	window.xmlhttp = null;
}

ん〜

微妙にひっかりがあるなぁ。
きっとプヨぷよ氏やSleipnir2スレの中の人たちがもっといい感じのをリリースしてくれることに期待。

やっとわかったPOSTの方法 つづき

やっとわかったPOSTの方法のつづきです。

Fiddlerをインストールしよう

今回はMicrosoft社のFiddler Web Debugger - A free web debugging toolを使ってみようと思います。僕自身もこのアプリの使い方はほとんど分かっていませんので知っている範囲のみで書いていきます。

まず、サクッとFiddlerをインストールして起動してください。

左側のサイドバーみたいなところにフォーカスを当ててCtrl+Xでリストをからにできます。
F12キーを押すとブラウザの通信をキャプチャするかしないかをOn/Offできます。左下のアイコンでその状態がわかります。
あとはちょこちょこあとで説明します。

POSTするリクエストボディを調べよう

ここではe-honを題材にしていきます。
とりあえず、Fiddlerを閉じるかF12を押してキャプチャをOffにしてさらにCtrl+Xしておいてください。

e-honのトップページ上部にある検索窓に「Fenrir」と入力して検索をクリックしてみてください。
Fenrir関連の書籍が検索されて出てきます。この検索フォームではPOSTメソッドが使われています。
では、もう一度Fiddlerを起動するかF12を押してキャプチャをOnにしてください。
今度は検索窓に「Sleipnir」と入力してけんさくしてみてください。
Sleipnir関連の書籍が検索されて出てきます。
ここでFiddlerを見てください。

こんな風になっていませんか?
人によっては導入しているスクリプトやプラグインとうで微妙にセッション数が違うと思いますが似たような感じになっていると思います。
ここで左ペインの一番上のセッションをクリックし、右ペインの上の”Session Inspector”タブをクリックし手ください。ここの”TextView”タブをクリックするとあらわれるのがリクエストボディです。

spKeyword=Sleipnir&scope=1&button=btnSpeed&mode=speed

よーく見ると"spKeyword="に続いて先ほど検索窓に入力した”Sleipnir”があります。ここの部分を変えることでいろいろなキーワードのリクエストボディを作れることがわかりました。

POST先URLは?

フォームのHTMLソースを見ればそれで済むのですがここではせっかくなのでFiddlerで。
今度は”TextView”タブではなく”Raw”タブをクリックしてください。

ここの12行目の”Host:”以降のハイパーリンク部分とここの一番上の行の

POST /bec/SA/Forward?cnt=1 HTTP/1.1

の”POST”と”HTTP/1.1”の間の部分とをつなぎます。

http://www.e-hon.ne.jp/bec/SA/Forward?cnt=1

これがPOST先のURLです。

やっと出てきた Navigate2メソッド

POSTで検索するための

  • リクエストボディ
  • リクエスト先URL

がそろったので本題の”Navigate2”メソッドです。

このメソッドによってPOSTします。
詳しくは僕には知識が足りなすぎるので以下のリンクを参考にしてください。
Navigate2 リファレンス
Excite エキサイト: ページが見つかりません

で、いきなりソースを貼り付ける

var engine = "http://www.e-hon.ne.jp/bec/SA/Forward?cnt=1";
var request = "spKeyword={all}&scope=1&button=btnSpeed&mode=speed";
var encode = "shift_jis";

var selectedText = document.selection.createRange().text;
request = request.replace(/{all}/, selectedText.replace(/&/, "%26").replace(/\?/, "%3F")); 
var strHeader = "Content-Type: application/x-www-form-urlencoded\r\n"; 

var WebBrowserObject = API.GetWebBrowserObject(API.GetDocumentID(API.ActiveIndex)); 
//WebBrowserObjectのNavigate2メソッドを使う
WebBrowserObject.Navigate2(engine, 0x1, "", ExEncode(request,encode), strHeader); 

function ExEncode(str, charset) { 
   var ads =  new ActiveXObject("ADODB.Stream"); 
   var res; 
    
   ads.Open(); 
   ads.Charset = charset; 
   ads.WriteText(str); 
   ads.Position = 0; 
   ads.Type = 1; 
    
   //UTF8への変換で先頭にBOMが付加されるのを除去. 
   //一回目でいきなり(Type設定前に?)3を設定するとエラー吐く。 
   if (charset == "utf-8") ads.Position = 3; 
    
   res = ads.Read(); 
   ads.Close(); 
    
   return res; 
} 


上のスクリプトはプヨぷよ氏のSearchActionCreatorが生成するスクリプトから抜粋・改変したものです。
フレームページ対策とかは全くやってないです。
11行目のNavigete2メソッドの第二引数に ”0x1” を指定する(ダブルクオートで囲まない)とSleipnirでは新規タブで開くようになります。

こうだったらなぁ。

文字コードエンコード)がやっかいです。もっと直感的にできるようなメソッドとかないのかなぁ。
Cookieもセットできるようなのですがどうもうまくいかない。 数字部分が欠けてしまうのはなぜだろう。

バテた

タイトル部分の説明はなし。
非常に尻すぼみなエントリ。

やっとわかったPOSTの方法

またまた、元ネタはプヨぷよ氏です。
SearchActionCreatorというUserActionスクリプトというのがあります。
UserAction スクリプト配布 - Fenrir User Community

詳しくは配布先で。




僕は前からスクリプトでPOSTし、レスポンスをsleipnirで表示したいと思っていました。GETメソッドなら簡単です。検索した結果表示画面のURLからクエリをみればOK
以下がその簡単なサンプルです。選択文字列をgoogleで検索します。

var document = _document;
var window   = _window;
var pnir     = new ActiveXObject("Sleipnir.API");

var copiedtext = document.selection.createRange().text;
pnir.NewWindow("http://www.google.co.jp/search?hl=ja&q="+copiedtext+"&btnG=Google+%E6%A4%9C%E7%B4%A2&lr=lang_ja",true);
//pnir.NewWindow("http://www.e-hon.ne.jp/bec/SA/Forward?submitLabel=%8C%9F%8D%F5%8AJ%8En&%8C%9F%8D%F5%8AJ%8En=btnKodawari&%83N%83%8A%83A=btnClear&mode=kodawari&shomei="+copiedtext+"",true);


//終了処理
window = null;
document = null;
pnir = null;

中にはサイト上ではPOSTで検索してるんだけど、リクエストクエリをそのままGETメソッドで送ってしまってもちゃんとレスポンスを返してくれるところもあります。(本当はこんなリクエストの仕方はよろしくないんだろうけど・・・)
上記スクリプトの6行目をコメントアウト(行頭に//と入力)して次の行のコメント行の先頭の//を削除して保存してください。
実行すると選択文字列をオンライン書店e-hon : 本 コミック 雑誌 通販で検索します。
e-honでは通常POSTメソッドで検索しますが、この通信をFiddler Web Debugger - A free web debugging toolFirefox+LiveHTTPHeaders拡張などを使ってリクエストボディを見ます。
POST先のURLとその文字列を?で繋げばOK
でも、クエリ文字が多すぎたり、サーバ側がはじいたり?するサイトではこの方法でもだめなんです。
前置きが長すぎてやっとエントリータイトルの内容まで進んできたのですが疲れてしまいました。
続きは近々書きます。