Win2KでSafari3.1動くじゃん!一応。2008年03月22日 04時53分07秒

あんまり関係ない前置きから

本エントリに直接関係ないのだが、このブログはずっとぐーぐる先生に嫌われていて、検索でここにたどり着くのはほとんどYahoo経由だったのだが、今日(つか昨日か)から突然ぐーぐる先生経由がゴスっと増えた。

んで、ちょうど3.1が正式リリースされたばかりなせいか、キーワードでわりとSafariが目立っていた。

会社のPC(XP Pro)にはさっそくインスコしていたのだが、「そういやSafari for Winでたばっかの時はむりやりWin2Kにインスコして悪戦苦闘してたよなぁ」と思い出したので、しつこくWin2Kに入れてみようかと。

懲りずにWin2Kにインスコしてみる

そんなわけで早速ダウンロードしてSafariSetup.exeを起動。iTunesは7になってからWin2Kを見捨てたので、「XPから対応だ。出直せ。」と警告の1つもでるかとわくわくしていたのだがあっさりインストール完了。

インストール後にすぐに起動してみると、なんの問題もなく起動。

スクリーンショット
って、このスクリーンショットじゃWin2Kで動いてるのわからんのだけど。

さすがに「β」が取れてるだけあって、3.0.xの時みたいな無理やりな対応を取らなくてもメニューもまともに表示されるし、当然ブックマーククラッシュもない。おまけにPreferences.plistをいじらなくても「開発」メニューを有効にできるし。なんだ、わざわざブログに書くことないじゃん

いや、実は3.0.2の時からもそうだったんだけど、tumblrのダッシュボードを開こうとするとログイン云々の前にかならずクラッシュするんだけどね。

これだけじゃ載せる意味ないので

2つばかり、気づいた点をば。

  • クラッシュするで。
    → 前出のtumblr/dashboardもそうだし、戯れに試してみたStartForceで表計算アプリ(EditGrid)開いたら何もいわずプロセスが消失&普通にクラッシュしたり。ここらへんは多分無理やりW2Kだからかなぁ。
  • Webインスペクタが使えない
    → これはちょっと残念。開発メニューから「Web インスペクタを表示」を一生懸命クリックしても華麗にスルーされる。同様に「エラーコンソールを表示」「ネットワークタイムラインを表示」もスルー。スニペットエディタだけは表示されるのだが。
    これ、当然 XP では問題なく利用できる。DOMツリーやらMetricsやら、かなり便利なだけに、本当に残念。つかXPつかえよ > 俺

まとめ

  • なんの苦労もなくインスコできるぞー。メニューだってちゃんと表示されるさー。
  • さくさく動くぞー。でもなんかの表紙にさくさくクラッシュするぞー。油断できないぞー。
  • 激しく便利なWeb インスペクタは使えないぞー。
→ まぁ、XP使っとけや。

オマケ・このブログのこれまでのSafariネタ一覧

列挙してみると、結構Safariネタ書いてたんだなぁ。ほとんどが役に立たん情報だが。

こんだけ書いてる割に「Safari」ってカテゴリ作ってないし。あとでちくちくカテゴリくっつけていくか。

オマケ2・StartForceのスクリーンショット

いや、ほんとはSafariで取りたかったんだけど、結局Firefox+JSAction+JSAction Button+capture.tumblr.jsで。

ブラウザと表計算アプリ2種(EditGridとZoho Sheet)。どちらもちゃんとSUM関数も使えてる。SafariじゃNGだったが。つか、表計算アプリ2つってえらい大盤振る舞いだな。

ブラウザでtumblrのダッシュボードを開いてみた。図ではわかりづらいけど、LDRizeもAutoPagerizeも機能している。ちゃんとjとかkも効く。mini bufferのコマンドは試してないけど。

「spam」指定の効能2008年03月26日 03時44分38秒

先日のエントリ

とあるので一応即削除せずにいったん「spam」指定して、半日くらい寝かせてから削除しているのだが、これってほんとに使われてるのかなぁ。
とかぼやいていたら、Days on the Moonのnanto_viさんからコメントをいただいた。

それによると、

3日から5日間でspam指定された情報を収集している
とのことなので、このくらいの期間は「spam」に設定して放置しておいたほうがよいのかも。

でも1日に数十通もスパム受けてると、ほかのトラックバックがまぎれちゃうし、spam指定したら管理画面から消えるなり別管理枠を設けるなりしてほしいなあ。

いや、ウチは普通のトラックバックなんて滅多にいただかないのでそれほど不便じゃないんだけど(w

それにしてもやっぱり「ASAHIネットで利用することがあります」ってのはなぁ。しっかり利用してくださいよ > ASAHIネット

2.3 * 100,000 のナゾ2008年03月27日 04時01分12秒

ちと意味わかんないんすけど

今日(あけてるので昨日か)の昼間、「PHPで 2.3を10万倍して int に cast したら誤差発生」という現象に見舞われた。最初はまたPHPのクサレ素敵仕様かバグかと思ったが、試してみたところ少なくともJavaScriptとC#(1.0)でまったく同様の結果になった。なので他の言語でもある話なのかも。

ちょっと理屈がわかんないんだよなー。

まずはすなおに出力

まずはこれ。

C:\Documents and Settings\dara-j>php -r "var_dump(2.3 * 100000);"
float(230000)

C:\Documents and Settings\dara-j>

まぁ、普通です。

キャストしてみる

んで、これをintにcastしてみる。

C:\Documents and Settings\dara-j>php -r "var_dump((int)(2.3 * 100000));"
int(229999)

C:\Documents and Settings\dara-j>

...えーっと...

もっともマニュアルの「浮動小数点」の説明を見ると、

こうなる理由のひとつとして、「有限小数に変換できない分数がある」 という事実があります。たとえば 1/3 を小数で表そうとすると 0.3333333. . . となります。

よって、小数の最後の桁を信用してはいけませんし、 小数が等しいという比較を行ってはいけません。より高い精度が必要な場合には、 任意精度数学関数または gmp 関数を代わりに使用してください。
とかあるので、まぁ精度の問題かしら、と思えなくもないのだが。

ちなみにJSでも

alert( Math.floor( 2.3 * 100000 ) );
で「229999」になる。

これは納得いかないんだが

こんどは1万倍と100万倍をやってみると、なんだか納得いかない。

C:\Documents and Settings\dara-j>php -r "var_dump((int)(2.3 * 10000));"
int(23000)

C:\Documents and Settings\dara-j>php -r "var_dump((int)(2.3 * 1000000));"
int(2300000)

C:\Documents and Settings\dara-j>
...なぜに意図どおりの結果が??いや、これで正しいんだけど。ってことは100,000だけ仲間はずれっすか。

さらに納得いかないんだが

よし、じゃあ10万倍が特別なのはよしとしましょう。でも、これは?

C:\Documents and Settings\dara-j>php -r "var_dump((int)(2.3 * 10 * 10000));"
int(230000)

...えええっと... えええっと...

内部での実行順序の違いってのがあるのかも知らんけど、* 100000と違う結果になるのはちとひどくないか?

この現象って、冒頭でも書いたけど別にPHP固有の現象ではなく、少なくともJavaScriptとC#ではまったく同じ結果になる。ちなみにここまでのコード例ではcastしてるけど、floor()使っても同じ結果になる。

10進型が使えりゃ気にしないのだが

実際、2.3 * 100000 で誤差がでると困るのでなんとか対応せにゃならなかったんだけど、さすがに * 10 してから * 10000 って、なんだかバッドノウハウくさいので、結局いったんstringを経由させてからintにキャストすることにしたんだけども。

こんな感じ。

function hoge($v) {
	// 元はこんな感じ
	// return (int)( $v * 100000 );
	
	// それをこんな感じに
	$v = $v * 100000;
	return (int)( "$v" );
	
	// なんというか、なんでこんなことを...
}

...なんだかなぁ。10進型(decimal)が使えればこんなことしなくてすむんだけどね。

あ、もちろんBCMath任意精度数学関数ってのがあるのは知ってるし、このケースで bcmul() なら問題ないことも確認してるんだけど、今回は実行環境にlibbcmathがなかったので。

オマケ

さらにさらに、これどうよ?

C:\Documents and Settings\dara-j>php -r "var_dump((int)(2.3 * 100 * 1000));"
int(229999)

C:\Documents and Settings\dara-j>php -r "var_dump((int)(2.3 * 1000 * 100));"
int(230000)

...ここまでくると、もはやどうでもいいや

おクスリトラバを強調するGMスクリプト2008年03月27日 12時53分48秒

こないだのスパムがらみエントリでも書いたが、アサブロのトラックバック管理画面で「spam」指定しておくとアサブロ側で情報を収集してくれるっぽいので即削除をやめてspam指定をするようにしてみた。

が、トラックバック管理画面はデフォルトで 10件 / 1ページ の表示なのであっという間に画面はspamだらけになり、管理しづらい。

なので、せめてもう少し見やすくしようとGreasemonkeyでユーザスクリプトを書いてみた。グリモンでしか確認してないが、ほかのブラウザのユーザスクリプトにも移植できるかも。そうでもないかも。

以下のスクリプトを、実行対象のページに「http://www.asablo.jp/app?cmd=edit_tb*」を指定しておくと、トラックバック一覧を表示したときに指定キーワードに一致する行の文字を赤(すでにspam指定済みの場合は暗い赤)で強調する。

var K = function(x) { return x; }

Object.extend = function(destination, source) {
	for(var key in source) destination[key] = source[key];
	return destination;
}

var $break = {};
var $continue = {};
var Enumerable = {
	each : function(iterator) {
		var i = 0;
		try {
			this._each( function(value) {
				try {
					iterator(value, i++);
				} catch(e) {
					if( e != $continue ) throw e;
				}
			} );
		} catch(e) {
			if( e != $break ) throw e;
		}
	},
	find : function(iterator) {
		var result = null;
		this.each( function(value, index) {
			if( iterator(value, index) ) {
				result = value;
				throw $break;
			}
		} );
		return result;
	},
	findAll : function(iterator) {
		var results = [];
		this.each( function(value, index) {
			if( iterator(value, index) ) results.push( value );
		} );
		return results;
	}
}
Array.prototype._each = function(iterator) {
	for(var i = 0, l = this.length; i < l; i++) iterator(this[i]);
}
Object.extend( Array.prototype, Enumerable );

Array.from = $A = function(list) {
	Object.extend( list, { _each : Array.prototype._each } );
	return Object.extend( list, Enumerable );
}

document.getElementsByClassName = function(className) {
	return $A( (arguments[1] || document).getElementsByTagName("*") ).findAll( function(ele) {
		var classes = ( ele.className || "" ).split(" ");
		return classes.find( function(c) { return c == className; } );
	} );
}

/* spam認定キーワード。うまく引っかからんやつもある */
var ng_words = /(drug|buy|price|cheap|tramadol|medication|side\seffects|hydrocodone|oxycodone|zithromax|phentermine)/ig;

$A(document.getElementsByClassName("list")[0].getElementsByTagName("tr")).findAll( function(tr) {
	return ng_words.test( tr.innerHTML );
} ).each( function(tr) {
	var state = tr.getElementsByTagName("select")[0].value;
	Object.extend( tr.style, {
		color : state == "spam" ? "brown" : "red"
	} );
} );

Enumerableだとか、prototype.jsの機能を一部実装してるのでちょっと長いが。

追記

ちなみにトラックバック一覧から絞込みや並べ替えやっちゃうと「http://www.asablo.jp/app?cmd=edit_tb*」にマッチしないのでこのスクリプト無効になっちゃいます。

もう少し使えるフィルタを実装してほしいなぁ > アサブロ

管理画面のプレビューリンクをポップアップ表示するGMクリプト2008年03月29日 05時11分34秒

コメントスパム、多すぎ

またもアサブロがらみのグリモンスクリプトです。

トラバもそうなんだけど、ここ何日かコメントスパムが増えてきた。しかもほぼエロ関係

コメントはいままで即時公開設定だったんだけど、非合法くさいエロサイトへのリンクが載っかってるのはさすがに気まずいっつーか気分悪いので、取り急ぎチェックを経てから公開するように変更したんだけど、いちいちプレビューを開いて確認するのはわりと大変なのでその場で確認してみたいと。

んでソース

前回のグリモンスクリプトよりもちょっと長いかな。

var K = function(x) { return x; }

Object.extend = function(destination, source) {
  for(var key in source) destination[key] = source[key];
  return destination;
}

var $break = {};
var $continue = {};
var Enumerable = {
  each : function(iterator) {
    var i = 0;
    try {
      this._each( function(value) {
        try {
          iterator(value, i++);
        } catch(e) {
          if( e != $continue ) throw e;
        }
      } );
    } catch(e) {
      if( e != $break ) throw e;
    }
  },
  find : function(iterator) {
    var result = null;
    this.each( function(value, index) {
      if( iterator(value, index) ) {
        result = value;
        throw $break;
      }
    } );
    return result;
  },
  findAll : function(iterator) {
    var results = [];
    this.each( function(value, index) {
      if( iterator(value, index) ) results.push( value );
    } );
    return results;
  }
}
Array.prototype._each = function(iterator) {
  for(var i = 0, l = this.length; i < l; i++) iterator(this[i]);
}
Object.extend( Array.prototype, Enumerable );

Array.from = $A = function(list) {
  Object.extend( list, { _each : Array.prototype._each } );
  return Object.extend( list, Enumerable );
}

document.getElementsByClassName = function(className) {
  return $A( (arguments[1] || document).getElementsByTagName("*") ).findAll( function(ele) {
    var classes = ( ele.className || "" ).split(" ");
    return classes.find( function(c) { return c == className; } );
  } );
}

Object.extend( String.prototype, {
  stripTags : function(br) {
    var result = this;
    if( br ) result = result.replace( /<br\s*\/?>/ig, "\n" );
    return result.replace( /<[^>]+\/?>/g, "" );
  },
  toElements : function() {
    var temp = document.createElement("div");
    temp.style.display = "none";
    document.body.appendChild( temp );
    
    temp.innerHTML = this;
    
    var result = $A( temp.childNodes );
    document.body.removeChild( temp );
    
    return result;
  }
} );

var pop = Object.extend( document.createElement("div"), {
  _hover : false,
  id : "popup_element_" + (new Date().valueOf()),
  visible : function() {
    return this.style.display != "none";
  },
  show : function() {
    if( this.visible() ) return;
    
    this.style.display = "";
    this.scrollTop = 0;
    var pos = {
      x : ( arguments[0] + 8 ) || this.clientLeft,
      y : ( arguments[1] + 8 )|| this.clientTop
    };
    this.style.left = pos.x + "px";
    this.style.top = pos.y + "px";
  },
  hide : function() {
    if( this._hover ) return;
    this.style.display = "none";
  }
} );
pop.addEventListener( "mousemove", function(evt) {
  pop._hover = true;
  evt.preventDefault();
}, true );
pop.addEventListener( "mouseout", function(evt) {
  pop._hover = false;
  setTimeout( function() {
    pop.hide();
  }, 1000 );
  evt.preventDefault();
}, true );

Object.extend( pop.style, {
  width : "350px", height : "150px",
  border: "solid 1px silver",
  backgroundColor : "whitesmoke", color : "dimgray",
  padding: "0px", fontSize : "8pt",
  position : "absolute", overflow : "auto",
  opacity : 0.9, display : "none"
} );
document.body.appendChild( pop );

var cache = {};

var handlers = {
  over : function(evt) {
    if( ! evt.target.href || ! /app\?cmd=preview/.test( evt.target.href ) ) return;
    
    var link = evt.target.href;
    if( ! cache[link] ) {
      var xhr = new XMLHttpRequest();
      xhr.open( "get", link, false );
      xhr.send( null );
      
      var container = xhr.responseText.toElements().find( function(node) {
        return node.nodeType == 1 &&
          /div/i.test( node.nodeName ) &&
          node.id == "container";
      } );
      var previews = document.getElementsByClassName("preview", container );
      if( previews ) cache[link] = previews[0].innerHTML;
    }
    pop.innerHTML = cache[link] || "oops !!";
    pop.show( evt.pageX, evt.pageY );
  },
  leave : function(evt) {
    setTimeout( function() {
      pop.hide();
    }, 1000 );
  }
}

$A(document.getElementsByClassName("list")[0].getElementsByTagName("a")).findAll( function(a) {
  return /app\?cmd=preview/.test( a.href );
} ).each( function(a) {
  a.addEventListener( "mousemove", handlers.over, false );
  a.addEventListener( "mouseout", handlers.leave, false );
} );


前半部分は前と一緒でprototype.jsの一部移植。ほんで「var pop = 」のところがリンク先をポップアップ表示させるツールチップにあたるオブジェクトの定義。

んでこのスクリプトを「http://www.asablo.jp/app?cmd=edit_*」で実行させる。そうすると「コメント一覧」「トラックバック一覧」と「記事の編集・削除」のそれぞれの画面のプレビューリンク先にカーソルを乗せてその場で内容を確認できる。こんな感じで。

プレビューリンクからカーソルはずすと勝手に消えるけど、ポップアップ表示してるところにカーソルのせとけばずっと表示されてるので多少はじっくり確認できます。