2007年 11月 24日

jautopagerize Safari (GreaseKit) 対応

かなり初期のころの挙動にもどした。innerHTML つかうのと変わらないなぁけっきょく。はてダみたいに head/link@href を見てるやつだと2ページ目以降とれない。ふつうのサイトは body 以下にも同じリンクがあるから、siteinfo のほうを変更することで対処できるけど……

あーなんかほとんどうごいてないなぁ。もういいや……Safari とか使わないし

Safari とは関係ないけどごちゃごちゃやってるうちに Fx + Google でちゃんと動くようになった……

XSLTProcessor

Fx で HTMLDocument つくるのに XSLTProcessor つかうようにしたんだけど、よくわからない条件でエラーがでる。

[Exception... "Component returned failure code: 0x80600001 [nsIXSLTProcessor.importStylesheet]" nsresult: "0x80600001 ()"

div + innerHTML + XPath 改変のほうが安定しそうだなぁ……なんでこのへんマトモな実装してないんだろ。意味不明すぎ (Safari3 と Opera には createHTMLDocument がある)

XSLTProcessor つかうのやめた。はてダででるんだよなぁ (はてグなら大丈夫)
id() についてはもともと Safari 向けのコードで //*[@id=''] に文字列置換するのを書いてる。二個以上 id 指定があるとだめだけどそんな使いかた普通しないから無視

Fx に createHTMLDocument がないのは createDocument に doctype わたせばいいってことらしいけど (というか DOM の仕様にないのはそういうことなんだろうけど)、

var html4dt = document.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd");
alert(document.implementation.createDocument("", "html", html4dt));

とかやっても普通に XMLDocument になるよ。イミフ。これってバグじゃないのかなぁ。でも Bugzilla には関わりたくない。

memo

  1. http://mxr.mozilla.org/firefox/source/content/base/src/nsDocument.cpp#706
  2. http://mxr.mozilla.org/firefox/source/content/base/src/nsContentUtils.cpp#3319
  3. http://mxr.mozilla.org/firefox/source/content/xml/document/src/nsXMLDocument.cpp#102

普通に XML の処理しか書いてない。

2004 年報告のやつだけどまだ NEW だ。NEW の意味わすれたけど

2007年 11月 23日

Fx3 対応の AutoPagerize

何度か AutoPagerize の書きかえに挑戦して挫折 (GP にうつってから AutoPagerize なしで生きてた) しているのでゼロから書きなおした。まだ途中だけど30%ルールぐらいでコミット (はてダ/はてグぐらいしか確認してないんだぜ……)

http://coderepos.org/share/browser/lang/javascript/userscripts/jautopagerize.user.js?

Mochikit の Deferred っぽいのをとおしてリソースのキャッシュとかを綺麗に書けるようにしてみた。コンパクトにしようとおもってたら API がちょっとキモくなってしまった。(メソッドチェインできることと、addCallback みたいに camelCase のどこを大文字にするのかわからないような名前をつけないことには気をつけた)

Fx3 の JS (JS1.8) には reduce があって嬉しいですね。あと 1.6-1.8 追加の関数は MDC に等価コードがのっているので、他のブラウザになくてもコピペすれば動くようになるので使いまくれる。

確認したかぎり最新の Greasemonkey (0.7.20071121.0) をつかってる。普通の GM だとまだ Fx3 にインストールできない。このバージョンでも locale が ja-JP だと動かないので en-US にしないとだめ。


セキュリティ関係で unsafeWindow.console.log とかできないっぽいのでデバッグがかなりめんどうくさい。深追いしてないからできるのかもしれないけど……あと GM_log も動いてないっぽい。


Safari (GreaseKit) とか Opera でも動くようにしたいのであんまり Fx 特化の技はつかいたくない (XSLT つかって HTMLDocument つくるとか……BK すぎる……)

console.log へ吐く方法はみつけた。

	function log (m) {
//		var c = unsafeWindow.console;
//		if (c) c.log.apply(c, arguments);
		var o = Array.prototype.concat.apply([], arguments);
		location.href = "javascript:(function () { if (window.console) console.log.apply(console.log, "+o.toSource()+") })();";
	}

カッコではじまる XPath はうまくいかない。根本的な解決はむずかしい。

なんで nextLink は属性値まで指定させる方法じゃないんだろ。form/@action とかのやつも siteinfo にそのまま書けるのに……

script 要素のタグは削除してるけど内容を削除してないのではてブ人気エントリとかでゴミが表示されてしまう。実害ないからとりあえず放置

AppleScript / RubyOSA++

require "rubygems"
require "rbosa"

safari = OSA.app("Safari")
safari.do_javascript("alert(location.href)", safari.documents[0])

jautopagerize の Safari 対応がなんかむりくさい感

toSource と reduce を実装して iframe で document とるようにするところまではうまくいくのだけど、そのあとなんか設定されているはずの変数が undefined になったりして意味不明すぎる (たまにうまくいく)。それにアクセスすると Safari がフリーズする。GC がおかしいのかなぁ。

なんかもっと別の方法考えないとだめなのかもしれない。(Safari は innerHTML すると html/body/link とか消してしまうので使えない)

ちなみに Fx3 で iframe つかって document とらないのはとれないからなんだけど、とる方法あるのかな。とれるならそのほうが簡単なんだけど

2007年 11月 22日

Fx3

http://lowreal.net/logs/2004/12/04/3 がなおってる。

2007年 11月 21日

Hpricot で一部の要素のみ通すフィルタ

require "rubygems"
require "hpricot"

def html_filter(parent, opts, &block)
	block = Proc.new {|x| x} unless block
	def escape(str)
		_escape = { '<'  => '&lt;', '>'  => '&gt;', '"' => '&quot;' }
		str.gsub(/#{_escape.keys.join("|")}/) {|m| _escape[m] }
	end

	ret = ""
	parent.each_child do |c|
		if c.elem?
			empty = Hpricot::ElementContent[c.name] == :EMPTY
			if opts.key?(c.name)
				ret << "<#{c.name}"
				ret << c.attributes.select {|k,v| opts[c.name].include? k}.map {|k,v| %| #{k}="#{escape v}"| }.join
				ret << "#{empty ? " /" : ""}>"
				ret << html_filter(c, opts, &block)
				ret << "</#{c.name}>" unless empty
			else
				ret << "&lt;#{c.name}"
				ret << c.attributes.map {|k,v| %| #{k}="#{escape v}"| }.join
				ret << "#{empty ? " /" : ""}>"
				ret << html_filter(c, opts, &block)
				ret << "&lt;/#{c.name}>" unless empty
			end
		else
			ret << block.call(c.to_s)
		end
	end
	ret
end

doc = Hpricot(inputs)
puts doc
puts html_filter(doc, opts) { |text|
	text.gsub(/¥n/, "<br />¥n")
}

こんなかんじかなぁ。ちょっときもいけど……


関係ないけど Mac でバックスラッシュをコピーして Fx にペーストすると円マークになってこまる。そのままペーストしてるけど、あとからコピペしようとすると使えないんだよね……

Ruby 1.9 のイテレータ (ちょーすごい)

で、そんなことより Mochkit.Iter の Synopsis の例を Ruby でやろうとおもったらいろいろ足りない感じ

(Enumerator は http://subtech.g.hatena.ne.jp/secondlife/20071104/1194144583 でちゃんと紹介されているよ! )

theSum = sum(takewhile(
        partial(operator.gt, 10),
        imap(
            partial(operator.mul, 2),
            count()
        )
    )
));

assert( theSum == (0 + 0 + 2 + 4 + 6 + 8) );

これこれ。

class Numeric
	def countup
		(self..(1.0/0)).to_enum
	end
end

module Enumerable
	def imap(&mapfun)
		Proc.new {|&block|
			self.each do |i|
				block.call mapfun.call(i)
			end
		}.to_enum(:call)
	end
end

# (sum はぬかしてる)
p 0.countup.imap {|x| x * 2}.take_while {|x| x < 10}

imap はなんか適当に書いてみたらこうなってしまったんだけどもっと簡単にかけたりするのかな…… 演算子の partial は定義しないといけないのと、ブロック書いたほうがはやいのでとりあえずとばし


countup はもっと簡単に書けないかとおもったけど微妙にめんどいっぽい。id:secondlife さんにもきいた。

# Float になっちゃう
p 0.to_enum(:step, 1.0/0).take(10)

# みじかいけど 1.0/0 (Infinity) がわかりにくい
p (0..(1.0/0)).to_enum.take(10)

# ながいよ
p lambda {|x,&block| loop { block.call(x); x += 1 } }.to_enum(:call, 0).take(10)

# Float::MAX が無限っぽくない
p 0.to_enum(:upto, Float::MAX).take(10)
2007年 11月 20日

ウェブサービスの XHTML 化

ユーザの入力をどうするか

  1. HTML を書けない記法しか許さない
  2. HTML を書けるがフィルタでとにかく well-formed にする

hpricot つかえば簡単に well-formed で XHTML にできてしまうなぁ。しかもはやい……

Hpricot(html, :xhtml_strict => true)

一部のHTML要素のみ通すフィルタ

ちがうちがうおれはなにをやっているんだ……

require "strscan"

# HTMLFilter
# 許した要素のみ残しながら、タグの対応を補完する
class HTMLFilter
	ESCAPE = { '<'  => '&lt;', '>'  => '&gt;', '"' => '&quot;' }

	EMPTY_ELEMENTS = ["br", "hr", "img"]

	def initialize(allow, callback=proc {|x| x})
		@allow = allow
		@callback = callback
	end

	def filter(input)
		ret = ""
		pool = ""
		s = StringScanner.new(input)
		elements = []
		until s.eos?
			if s.scan(%r{<(/)?(#{@allow.keys.join("|")})}i)
				ret << @callback[escape(pool)]
				pool = ""
				name = s[2]
				if s[1]
					# end tag
					s.scan(%r{¥s*>})
					while opened_but_close = elements.pop
						ret << "</#{opened_but_close}>"
						break if name == opened_but_close
					end
					# remove the end tag if it was not opened.
				else
					# start tag
					attrs = []
					while s.scan(%r{¥s+([a-z-]+)=(?:"([^"]*)"|'([^']*)')}i)
						an = s[1]
						av = s[2] || s[3]
						attrs << "#{an}='#{escape(av)}'" if @allow[name].include?(an)
					end
					attrs = attrs.empty?? "" : " #{attrs.join(" ")}"
					empty = false
					if s.scan(%r{¥s*(/)?>})
						case
						when EMPTY_ELEMENTS.include?(name)
							empty = true
							ret << "<#{name}#{attrs} />"
						when s[1]
							empty = true
							ret << "<#{name}#{attrs}></#{name}>"
						else
							ret << "<#{name}#{attrs}>"
						end
					else
						# invalid but continue
						ret << "<#{name}#{attrs}>"
					end
					elements.push(name) unless empty
				end
			else
				pool << s.getch
			end
		end
		ret << @callback[escape(pool)]
		ret << "</#{opened_but_close}>" while (opened_but_close = elements.pop)
		ret
	end

	def escape(str)
		str.gsub(/#{ESCAPE.keys.join("|")}/) {|m|
			ESCAPE[m]
		}
	end
end

opts = {
	"a"      => ["href", "name"],
	"strong" => [],
	"br"     => [],
	"p"      => [],
	"ins"    => ["datetime"],
	"del"    => ["datetime"],
}

inputs = DATA.read
out = HTMLFilter.new(opts, proc {|str|
	str.gsub(/¥n/, "<br />¥n")
}).filter(inputs)

puts out



__END__
<script foo="<script>alert('bar')</script>">alert('foo')</script>
<script foo="<a href='link'>link</a>">alert('foo')</script>
<a href='www.g>oogle.com'>link</a>
<a href="hoge" name="aaa">hoge<strong style="">ttt</strong></a>
<a href="hoge" name="aa
{a">hoge<br><br/></a>

<ins datetime="">
<p>
aaa
</p>

<p>aaa

</ins>

<a></strong>
<p>aaa

どう書く?org のお題 をやっていたんだった。なんかだんだんズレてきたので投稿がためらわれる。改行を br にしろというお題 に対応ずみ。