jAutoPagerize / Bug in Safari
twitter とか、prototype.js をつかっているサイトだと Array#reduce がうわがきされるうえに挙動がちがっててうごかない。
こっちの prototype 拡張をやめる (関数よびだしにする) っていうの以外で解決できるならするけど、できないならやらない。
twitter とか、prototype.js をつかっているサイトだと Array#reduce がうわがきされるうえに挙動がちがっててうごかない。
こっちの prototype 拡張をやめる (関数よびだしにする) っていうの以外で解決できるならするけど、できないならやらない。
http://coderepos.org/share/changeset/2041
ごくごく簡単な XPath ジェネレータ/チェッカをくみこんでみました (Safari でのデバッグ用途が主目的)。siteinfo つくるなら基本的に http://labs.gmo.jp/blog/ku/2007/07/autopagerizexpath_autopagerize_ide.html をつかえばいいとおもうのでいらないと思います (さっきまで AutoPagerize IDE つかったことなかったw こりゃ便利ですね><)。
jAutoPagerize のアイコンをクリックするとテキストエリアがでます。Inspect ボタンをおすと要素選択して XPath を生成します。テキストエリア編集すると選択ノードにアウトラインがつきます。Safari でも動きます。jAutoPagerize のアイコンが表示されてないところでは javascript:XPathGenerator.open(); するとでてきます。
ノードから XPath つくるやつはちょー簡単に生成するのを前書いていたらしくて (test.user.js で getXPath... までうちこんだら autocompletepop された……) それをちょい改善してつかいました (ほとんどテストしてないからバグってるかも)。
getXPathByElement : function (target) {
var pathElement = "";
var node = target;
if (node.nodeType == 9 /*DOCUMENT_NODE=9*/) {
return ""
} else {
var tagName = node.tagName.toLowerCase();
if (node.hasAttribute("id")) {
// pathElement = tagName + '[@id="'+node.getAttribute("id")+'"]';
pathElement = 'id("'+node.getAttribute("id")+'")';
} else {
pathElement = arguments.callee(node.parentNode) + '/' + tagName;
if (node.hasAttribute("class")){
pathElement += '[@class="'+node.getAttribute("class")+'"]';
} else {
pathElement += '['+indexOf(node)+']';
}
}
}
return pathElement;
function indexOf (node) {
for (var i = 0, r = 1, c = node.parentNode.childNodes, len = c.length; i < len; i++) {
if (c[i].nodeName == node.nodeName &&
c[i].nodeType == node.nodeType) {
if (c[i] == node) return r;
r++;
}
}
return -1;
}
}
Ruby にしても JS にしても $1 $2 は、それらがどっから沸いてきたのかソースコードをうえからよんでいったときにわかりにくいので、できるだけ使たくない。暗黙的に代入されているし、$1 $2 っていう名前には全くマッチを連想させない (慣れればわかるけど……あと JS の場合は RegExp.$1 とかになるのでいくぶんマシ)。しかも $0 は実行中のプロセス名だから $0 と $1 の間に同じ $n なのに全く関連がない。
よっぽどめんどくさくない限りは Regexp.last_match したり m = str.match(r); する。(Perl のときは気にしない)
GreaseKit だと同じウィンドウで userjs が実行されるので、常に prototype 上書きされる危険性があって、これはどうしよもない。普通は標準の関数をわざわざうわがきしたりしないから問題ないけど、前述の Array#reduce みたいに、微妙な立場の関数でしかも名前がよくつかわれるやつは上書きされやすい。(というか実際には Array#map も上書きされているけど、こっちは挙動が同じなので問題になってないだけ)
Opera もたぶん同じウィンドウで実行されるから同じ問題をもっていると思うけど、あんまり調べてない。
解決法:
あとたぶんこういう関係 (unsafeWindow的な意味で) jAutoPagerize + Safari + GreaseKit だと XSS ふんでそうな感じがするのでチェック必要だと思う。(ちょっと考えたけどとれちゃうな…… http://coderepos.org/share/changeset/2050 修正した。ほかにもある予感……)
(Greasemonkey だとウィンドウが別れているのでだいだい問題おきない。なぜ unsafeWindow が GM にはあって、他のにはないのかをよく考えて使わないとだめですね><)
あれ r2050 じゃ解決になってないよね…… siteinfo を外からアクセスできるようにしたらだめだな。
r2051 でたぶん解決。Safari でつかうのは危険なかおりがプンプンする。
nanto_vi さんのコメントで原因が判明したので試行錯誤してコミットした。
http://coderepos.org/share/changeset/2053
var x = new XSLTProcessor();
var t = [
"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>",
"<xsl:output method='html'/>",
"<xsl:template match='/'>",
"<html><head><title>", title, "</title></head><body/></html>",
"</xsl:template>",
"</xsl:stylesheet>",
].join("");
var d = document.implementation.createDocument("", "nice-boat", null);
var r = d.createRange();
r.selectNodeContents(d.documentElement);
d.documentElement.appendChild(r.createContextualFragment(t));
x.importStylesheet(d.documentElement.firstChild);
var ret = x.transformToDocument(d);
なんかしばらく新しく siteinfo をとりにいってキャッシュするところがうごいてなかったっぽい。なおした。
でも Safari だといまいち挙動不審で、たまにsiteinfo 全部 ({}) になってしまうことがある。textarea.replace(//, function () {}) がへんなのかも……めんどい
uneval を適当に実装してテストしてたらきづいた。
ひさしぶりに何か想像を書こうと思ったのだけれど書けなかった……
position: absolute で段組されてるところだと document.documentElement.scrollHeight がすごく小くなってしまい、ページがどのぐらい残っているか取得できない。
あと GM_xmlhttprequest をつかうべきじゃないかも。ページングがクロスサイトすることなんてまずないし、無用な特権は行使しないほうがいいよね。
userChrome.js + http://www.ne.jp/asahi/nanto/moon/2006/12/31/copy-url-lite.uc.js + Firebug
{
label: "Copy XPath",
accesskey: "X",
get text () {
return FBL.getElementXPath(document.popupNode);
}
}をつけたして C-n して新しいウィンドウを開く /html/body/div[2]/div[2]/form/div/textarea
ただし、Fx の場合 thead とかが存在しなくても DOM 上では thead を補完するので、そのままでは他のブラウザでとおらないこともあるとおもう (Google のページングとかそうだけど AutoPagerize 的なときに困るのでちゃんと確認してほしい)。
http://d.hatena.ne.jp/keisukefukuda/20071126/p1
Perl と同じように (つまり libxml そのまま) ではありませんが以下のようにしてできます。
require "rubygems"
require "xml/libxml"
require "tempfile"
f = Tempfile.new("xml")
f << <<EOS
<?xml version="1.0" encoding='utf-8'?>
<service xmlns="http://www.w3.org/2007/app"
xmlns:atom="http://www.w3.org/2005/Atom">
<workspace>
<atom:title>Main Site</atom:title>
<collection href="http://example.org/blog/main">
<atom:title>My Blog Entries</atom:title>
<categories href="http://example.com/cats/forMain.cats" />
</collection>
</workspace>
</service>
EOS
f.close
doc = XML::Document.file(f.path)
namespaces = {
"app" => "http://www.w3.org/2007/app",
"atom" => "http://www.w3.org/2005/Atom"
}.to_a
p doc.find('/app:service/app:workspace/app:collection/atom:title', namespaces)
#=> <atom:title>My Blog Entries</atom:title>namespaces にわたすのが配列なのが微妙にひっかかる。(ドキュメントよむと "prefix:uri" みたいな文字列でもわたせるけど圧倒的なキモさなので使わないとおもう)
バズワード化してる。もとからかw
Ruby のブロックは実のところ proc {} を引数に渡すのを、その proc が一つの場合に限って簡略化して書けるだけなのだけど (実際はちょっと違うっぽいけど)、これがかなりうまくいってるのがおもしろいなぁ。
ってのはすごく重要だなぁ。
class TrueClass
def then
@ret = yield
self
end
def else
self
end
end
class FalseClass
def then
self
end
def else
@ret = yield
self
end
end
(foo == bar).then {
puts "hoge"
}.else {
puts "fuga"
}ふつうに綺麗に書ける。
Boolean.prototype.then = function (fun) {
// this は Object になっているので true になってしまう。
// もとの値をとりだすために valueOf()
if (this.valueOf()) fun();
return this;
};
// else は予約語なのでつかえない
Boolean.prototype.els = function (fun) {
if (this.valueOf()) fun();
return this;
};
("foo" == "bar").then(function () {
print("hoge");
}).els(function () {
print("fuga");
});ぜんぜん綺麗じゃない。
ちなみに Io だと if([pred], [then], [else]) 以外にもうえのと同じことがデフォでできる。(Io では引数は基本的に評価されない。Message オブジェクトとしてメソッドにわたる。ただし仮引数を書いた場合は自動で評価される)
("foo" == "bar") then (
"hoge" println
) else (
"fuga" println
). ではなくスペースをメッセージ送信のくぎりにすることはこういうところで生きてきますね。一見したら構文にしか見えない。(if は then/else の評価されたほうの値をかえすけど、else は nil をかえす。then はもちろん self をかえしている)
ラムダ計算では true/false はもちろん関数で、こんなかんじらしい (true は x と y を引数にとり x を返す関数で、false は y を返す関数)
t :: a -> b -> a
t x y = x
f :: a -> b -> b
f x y = y
main :: IO ()
main = t (putStrLn "hoge") (putStrLn "fuga")Haskell で書いたはいいけど、あんまわかりやすくないなw (引数は評価されるけど、副作用はモナドでラッピングされてる(?)から、かえされた IO モナドインスタンスが保持してる putStrLn "hoge" しか表示されないってことだとおもうけど、というか用語がすでにあやしい。Haskell こわい)
(define (t x y) x)
(define (f x y) y)
((t
(lambda () (print "hoge"))
(lambda () (print "fuga"))))Scheme で書くとこんなかんじかな。Haskell より綺麗に書けないけど、なんかまちがってるかな……(引数は評価されてしまうので lambda でかこみ、t の返り値をさらに呼びだしてる)
まぁどっちにしろこれってどことなく OOP で書いた
true then ( "hoge" println ) else ( "fuga" println )
に似ていて、
true if := method(
call evalArgAt(0)
)
false if := method(
call evalArgAt(1)
)
("foo" == "bar") if (
"hoge" println
,
"fuga" println
)とかくともっと似てますね! と最近思ったのでした。(もしかしたらあたりまえなのかもしれないけど、じぶんとしては、やっと気付けたことなのでした)
似ているからなんなの? っていうのはまだうまく言語化できない。なんというか、「ああそっかラムダ計算もオブジェクト指向も純粋だと似てくるんだなぁ」みたいな、よくわからないけど
あー Ruby で書くともっとわかりやすいかなぁ……
# lambda
t = lambda {|x, y| x }
f = lambda {|x, y| y }
t[lambda {
puts "hoge"
}, lambda {
puts "fuga"
}][]
# OOP
class TrueClass
def if(x, y)
x[]
end
end
class FlaseClass
def if(x, y)
y[]
end
end
true.if(Proc.new {
puts "hoge"
}, Proc.new {
puts "fuga"
})ラムダのほうは似せるために curry 化してない。curry 化するとラムダのほうはもっと綺麗にかける。
# http://subtech.g.hatena.ne.jp/cho45/20071119/1195420784
t = lambda {|x, y| x }.curry
f = lambda {|x, y| y }.curry
t[lambda {
puts "hoge"
}][lambda {
puts "fuga"
}][]curry 化バージョンは Scheme と全く同じことをやってる。
というか Ruby は表現しやすいなぁ。Io や Scheme や Haskell は初見だと読めないし……Ruby にしとくと関数系の人もOOP系の人も読めていいかんじがする。
OOP ← Io - Ruby - Scheme - Haskell → ラムダ
この4つの言語やるとなんかいろいろわかる気がしてきた。JavaScript は return かかないといけないからここには入れたくないな (OOP にしたって関数にしたって return かかないといけないのは害にしかならない)
ラムダきわめると Lazy-K になるっぽいけど、OOP (メッセージセンド) を極めるとどうなるんだろう。Io でいいのかな。たしかに全部メッセージセンドだけど、違和感ある。なんでだろう。
頭にうかんだ順に書けるぶん、自分は関数的に書くより OOP で書くほうが好きだ。
# http://subtech.g.hatena.ne.jp/cho45/20071121/1195642614
0.countup.imap {|x| x * x }.take_while {|x| x < 10}import Text.Show
countup = iterate (\x -> x + 1)
main :: IO ()
main = putStrLn . show $
takeWhile (< 10) $ map (\x -> x * x) $ countup 0メソッドチェインだと前から順番によんでいけるけど、関数よびだしだと頭の中のスタックに前から順につんでポップしていかないといけない。
関数系の人はたぶん違う方法で考えているんだろうなぁ……
でも関数の部分適用とかはたのしい うえのコードの (< 10) とか、countup の定義とかすばらしくたのしい。
あーいやうーん。OOP と関数の比較っていうのとは違うかんじもする。
http://rubycocoa.sourceforge.net/RubyInject
これのせいで俄然 Leopard が欲しくなった。
たぶんプロトタイプベースな Ruby がほしいのかもなぁ。
self #=> Global env. Object
hoge = "hoge"
self.hoge = "hoge"
Global.hoge = "hoge"
Global.Global #=> Global
Global.proto #=> Object
Global.fun #=> fun {|&block| [native] }
Object.foo = fun {
self #=> a env. object having receiver as proto
}
Array.map = fun {|&f|
ret = []
self.each {|i|
ret << f(i)
}
ret
}
String.each = fun {|&f|
split("").each(&f)
}
String.map = Array.slot("map")
String["map"] = Array.slot("map")
String.set_slot("map", Array.slot("map"))
String["[]="]("map", Array.slot("map"))
Array.clone.set(1, 2, 3) == [1, 2, 3] == Array.with(1, 2, 3)
o = Object.clone.do {
self #=> just the `o`
hoge = "hoge"
self.foo = "foo"
fuga = fun {|x|
"fuga"
}
}
o.hoge #=> "hoge"
o.foo #=> "foo"
o.fuga #=> "fuga"
o.slot("fuga") #=> fun {|x| "fuga" }
Array.sum = Array.slot("inject").partial(0) {|r,i| r + i}
[1, 2, 3].sum #=> 6括弧の省略をしても呼びだしされるあたりがなんかうまくいかなそう。文法の矛盾があったりして、寝ておきたら「だめじゃんw」と思うんだろうなぁ。
Ruby を書いててときどき、 JS みたいに関数を自由にあつかいたいと思う。Ruby はクラスベースで、メソッドはかならずどこかのクラスに属し、UnboundMethod は bind しないかぎりよべず、bind の対象ももとのクラスと関係がないといけない。だから……だからなんだろう……
うまくまとまらない。
したいこと
あーふつうに GM_log つかえてた……
Firebug 1.0 以降では extensions.firebug.showChromeMessages を true にしないと GM_log のメッセージが表示されないらしい。true にしたらちゃんとでた……
toSource のラッパっぽい uneval っていう関数があるみたいだ (いつからあるんだろ)。toSource は null とか undefined のときエラーになるけど (オブジェクトじゃないから) これだとならない。
uneval(null); //=> "null"
uneval({1:undefined}); //=> "({1:(void 0)})"
http://malblue.tumblr.com/post/20163446
かなりまえからあるらしい。(1.5) String.prototype.quote とかいうのもあるらしい。Ruby の String#dump 相当かなぁ
贅沢言わなければ動くようになったよ。Safari の innerHTML 系メソッドは link, meta も消しちゃうので、それを nextLink に指定していなければ動くよ。ただ、なぜかユーザスクリプトが実行されないページがあるので要検証
http://userscripts.org/scripts/show/14666
「もういいや」とか書くとなんか続けたくなる謎