Hatena::ブログ(Diary)

bonlife このページをアンテナに追加 RSSフィード

言及ISBN/ASIN
  • Ringo EXPO 08 [DVD]
  • 三文ゴシップ
  • my way
  • ビジネスパーソンのための話し方入門 (日経文庫)
  • ザ・グーグルウェイ グーグルを成功へ導いた型破りな戦略
  • 考え・書き・話す3つの魔法
  • 自分の答えのつくりかた―INDEPENDENT MIND

2008-07-19 靴のかかとが血まみれです。

Google Ajax Search APIで検索結果数を取得(したけどかなりあやしい)

bonlifeです。せっかくの三連休なので、いつもと違うことをやってみよう!ということでGoogle Ajax Search APIを使ってみました。興味ある方は「AJAX Search API キーへの登録 」をした後で、試してみてくださいませ。 (下記のソースの YOUR_API_KEY_HERE の部分を置き換えてくださいな。)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>estimatedResultCountが非常にあやしい</title>
    
    <style type="text/css">
    body {
      background-color: white;
      color: black;
      font-family: Arial, sans-serif;
      font-size : 13px;
      margin: 15px;
    }

    #searchcontrol .gsc-control { width : 400px; }
    </style>
    <script src="http://www.google.com/jsapi?YOUR_API_KEY_HERE" type="text/javascript"></script>

    <script type="text/javascript">
    //<![CDATA[
    
    google.load('search', '1.0', {"language" : "ja"});

    function OnLoad() {
      var searchControl = new google.search.SearchControl();
      searchControl.addSearcher(new google.search.WebSearch());
      searchControl.setSearchCompleteCallback(searchControl,showEstResults);
      searchControl.setNoResultsString("そんなもんない!");
      searchControl.draw(document.getElementById("searchcontrol"));
      searchControl.execute("bonlife");
    }

    function showEstResults(seachControl,searcher){
        if (searcher.cursor == undefined) {
            document.getElementById("resultcount").innerHTML = 0;
        } else {
            document.getElementById("resultcount").innerHTML = searcher.cursor.estimatedResultCount;
        }
    }

    google.setOnLoadCallback(OnLoad, true);

    //]]>
    </script>
  </head>
  <body>
    <div id="searchcontrol">Loading</div>
    <div id="resultcount">Loading</div>
  </body>
</html>

これやってみると分かりますが、普通にGoogleで検索した場合と検索結果の件数がおそろしく違う!

.estimatedResultCount - 現在のクエリと一致する結果の数の推定値を指定します。この値は Google.com 検索プロパティに表示される類似の値と必ずしも一致しないことに注意してください。

Class Reference - Google AJAX Search API - Google Code

こう書いてあるけど、それでも…。どっちの値があやしいのか分かりませんが、全く違ってイヤになっちゃうワン。というダメ出しは置いておいて、かなりお手軽にリッチUIの検索窓を置けますね。ビックリ。

2008-07-03 少しスッキリしました。

リッスンジャパンの中間ページを飛ばすやつ

もうすぐ七夕ですね、そうですね。ということで超お久しぶりのbonlifeです。

ニュースの一覧から、記事を選ぶじゃないですか。そうすると、記事の途中までしか表示されなくて、全文を読もうとすると「ニュース全文を読む」ってのをまた押さなきゃいけないんですけど、なんとかならないですかね。

と、書いておけば願いが聞き届けられたりしませんかね、もうすぐ七夕ですし。

リッスンジャパンでですね - htpr

そういうのってFirefox使ってたらGreasemonkeyだよね!ということで、慣れないJavaScript書いてみましたよ。

// ==UserScript==
// @name           Skip meaningless news pages of listen.jp
// @namespace      http://d.hatena.ne.jp/bonlife/
// @description    overwrite hyperlinks of intermediate news pages
// @include        http://listen.jp/*
// ==/UserScript==

function xpath(query) {
	return document.evaluate(query, document, null,
	XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}

var a_list = xpath("//a[contains(@href,'/store/musicnews_')]");
for (var i = 0; i < a_list.snapshotLength; i++ ){
    target_a = a_list.snapshotItem(i);
    new_href = target_a['href'].replace(/_(\d+)(\.html?)/,'_$1_all$2');
    target_a.setAttribute("href",new_href);
}

これで↓こういうリンク

http://listen.jp/store/musicnews_24256.htm

こう書き換えられるわけですよ。

http://listen.jp/store/musicnews_24256_all.htm

でも、リッスンジャパンってFirefoxだと試聴ができないマジック!!SleipnirSeaHorseでやれば良いのかな、とか思いましたが、面倒なのでパス…。

2008-02-13 寒くて死にそうです。

Selenium IDEで1つ1つのテストの処理時間を計測 (悪戦苦闘)

bonlifeです。Oracleの設定変更するとかなんとかで、Webアプリケーションのテストをちょっとだけ担当することになったので、Selenium使ってみました。普通はサーバにデプロイ(って単語がなんか強そう!)して使うと噂のSelenium。諸事情あってサーバには導入し辛いので、Firefoxの拡張として使えるSelenium IDEで頑張ってみましたよ。

Automating Selenium IDE tests - Selenium IDE - Confluence」あたりを参考にして色々と試して感動!!でも、なんだかテストのログの残し方がよく分からない…。[Show Log]とかしておけば画面にログが出るので、それをコピーすれば良いかな、と最初は思ってたのですが、store*した変数の値とかまで出力されるわけじゃないっぽいので、なんかちょっと違う感じ。このあたりに詳しそうな人もまわりにいなかったので、悪戦苦闘しまくりながら、自分でなんとかしてみました…。

今回はOracleの設定変更で処理速度が低下していないことを確認したいので、時間計測が超重要。(Oracle 11gだったらそういうのに持ってこい!な「Real Application Testing」があるんですよね、確か。) 処理開始時のDate()をstoreEvalでstart_timeって名前付けて取っておいて、処理後のDate()の結果(end_time)との差を出せば、処理時間GETだぜ作戦。(なんかマニュアルをもっとちゃんと読めば書いてありそうですけどね…。TestSuite全体の処理時間はブラウザにも表示されますが、1つ1つのテストの処理時間については載ってなかった気がします。)

以下がテストケースの例。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>case01</title>
</script>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">case01</td></tr>
</thead><tbody>
<tr>
	<td>storeEval</td>
	<td>new Date().getTime()</td>
	<td>start_time</td>
</tr>
<tr>
	<td>open</td>
	<td>/cgi-bin/hoge.cgi</td>
	<td></td>
</tr>

<tr>
	<td>clickAndWait</td>
	<td>search</td>
	<td></td>
</tr>
<tr>
	<td>assertText</td>
	<td>xpath=/html/body/table[2]/tbody/tr/td/p/text()</td>
	<td>hogehoge</td>
</tr>
<tr>
	<td>storeEval</td>
	<td>new Date().getTime()</td>
	<td>end_time</td>
</tr>
<tr>
	<td>storeEval</td>
	<td>Math.round((storedVars['end_time'] - storedVars['start_time']) / 1000)</td>
	<td>elapsed_time</td>
</tr>
<tr>
	<td>storeEval</td>
	<td>doWriteResult('case01', storedVars['elapsed_time'])</td>
	<td>dummy</td>
</tr>
</tbody></table>
</body>
</html>

処理時間を取得しても、どこかに残しておかないとアレなので、今回はファイルに書き込むことにしました。それが上の doWriteResult('case01', storedVars['elapsed_time']) の部分。JavaScriptの変数のスコープとかもよく分かってないでアレですが、1つのテストの結果を変数に格納しておいて次のテストで使ったりするのが私には難しかったので独自に関数を定義して、ファイル書き出しですよ。

以下のように doWriteResult() を定義しています。JavaScriptでのローカルファイルの操作が思いのほか難しくて死ぬかと思いました。が、TiddlyWikiのソースコードを(丸々)参考にしてなんとかでっち上げましたよ! (なんとなく do で始まる関数が良いんだぜ、敵な箇所がマニュアルにあったので、そこを真似したり失敗してる内に変な関数名になってしまいましたが、ご愛嬌。Selenium.prototypeを拡張する場合だったかな。よくワカリマセン…。)

  • output_elapsed_time.js
doWriteResult = function(testcase_number, elapsed_time) {
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
    today = new Date();
    yyyy  = today.getFullYear();
    mm    = today.getMonth() + 1;
    dd    = today.getDate();
    if ( mm < 10 ) { mm = "0" + mm; }
    if ( dd < 10 ) { dd = "0" + dd; }
    today_YYYYMMDD = yyyy.toString() + mm.toString() + dd.toString();
    file.initWithPath("C:\\selenium\\test\\elapsed_time_" + today_YYYYMMDD + ".txt");
    if(!file.exists()) { file.create(0,0664); }
    var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
    out.init(file,0x02 | 0x10,0664,null);
    elapsed = testcase_number + "\t" + elapsed_time + "\n";
    out.write(elapsed, elapsed.length);
    out.flush();
    out.close();
};

用途は限られているのでフォルダのパスは固定。日をまたがってテストをすることもなさそうなので、ファイル名には YYYYMMDD を付与。で、ブラウザのURL欄に以下のような文字列をコピペして実行ですよ。

chrome://selenium-ide/content/selenium/TestRunner.html?baseURL=http://www.example.com/&test=file:///C:/selenium/test/test_suite.html&userExtensionsURL=file:///C:/selenium/test/output_elapsed_time.js

う、動きました…。ファイルに処理時間(秒)がたんたんと記録されました。妙な達成感ですよ!!でも、でもでも。本当は user-extensions.js とかで拡張したり、なんかもうちょっとマシなやり方があると思うのですが、どうでしょう。教えてエロイ人!! (なんだか激しく勘違いをしていそうな予感です。)

2006-02-16 安藤裕子はブレイク済みなのか、これからブレイクするのか。

JavaScriptでウィンドウサイズに合わせて画像サイズを変更

人力検索はてなの質問(http://www.hatena.ne.jp/1140032618)の回答が現時点(2006/02/17 00:45)で1件開かれています。そこで示されているサンプルで、およそ希望していることは出来そうな雰囲気なのですが、個人的に引っかかる部分があったので試行錯誤しながらJavaScriptを書いてみました。何が気になったのかと言うと、リサイズの仕方です。縦横比を維持せずにリサイズするのがどうも生理的にダメなのです。そこで、画像の縦と横のどちらが画面に収まらないのかをチェックし、縦横比を(ほぼ)維持したまま画像をリサイズするJavaScriptにしてみました。IE6.0とFirefox1.5で稼動確認いたしました。もちろん画面表示上のサイズを小さくするだけですので、画像のファイルサイズは一切変わりません。本来、ウィンドウサイズをあらかじめ考慮しておき、そのサイズにふさわしいサムネイル画像を用意するべきですよね。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<title>ウィンドウサイズに合わせて画像サイズを変更</title>
<script type="text/javascript">

</script>
<style type="text/css">

</style>
</head>
<body onLoad="imgResize()">
<img src="./XXXXXX.jpg" id="image1" onClick="window.open('./XXXXXX.jpg','imgOriginalSize')">
</body>
</html>

このスクリプト、残念ながら1点大きな欠点があります。一度ウィンドウサイズを小さくして画像表示を小さくした後、ウィンドウサイズを広げても画像が大きくなりません。この理由は document.getElementById('image1').width で取得する値がリサイズ後の値になってしまうことです。元の画像サイズそのものを取得しているわけではなく、あくまでも表示上のサイズを取得しているんですよね…。おそらく、最近ではタブブラウザを使っている人も多く、ウィンドウサイズを頻繁に変更する人は少ないと思いますので、初回表示時に画像が画面に収まれば問題ないとは思うのですが、どうにも気持ち悪いです。残念。(有識者の皆さん、「こうすれば良いんじゃないの。」などのご助言お願いいたします。)ということでオマケでonClickで元画像を開くようなソースにしてみました。最近のブラウザは画像ファイルを開くと自動的にウィンドウに収まるようになっていますが、この機能で十分かもしれません。そんなこんなで回答するのは控えておきました。

ちなみにFirefoxではCSSでbodyタグに設定しているmarginJavaScriptで設定するmarginを同じ値にして正しく動作したのですが、IEではなぜか縦のスクロールバーが出てしまったため、JavaScript内で指定しているmarginの値を少し大きめにしておきました。このあたりもダサいですね…。

2006-02-10 配列って奥が深いですね。

JavaScriptで複数のcheckboxの状態をチェックし、PHPで表示

人力検索はてなでの質問(http://www.hatena.ne.jp/1139557333)の回答を少し修正しました。PHPでデータを処理させる場合、配列になる項目のnameの末尾に括弧をつけておく必要があるのですが、これをJavaScriptで処理しようとするとちょっとややこしいのです。JavaScriptでは括弧を付けて明示的に配列にしているつもりでも、値が1個しかない場合には配列として扱われず、lengthなどのメソッドが使えないのです。そこで、値があるかないか、配列かどうか、と二重のチェックをする必要があります。これがちょっと面倒…。ここのサイト(http://web.paulownia.jp/script/tips/array.html)が参考になりました。

回答ではchecked状態になっているcheckboxの数を数える際、nameとcheckedを分けて以下のように記述していました。

if ((document.myForm.elements[i].name==”flag[]”) && (document.myForm.elements[i].checked)) {
    count ++;
}

ところが、よくよく調べてみると以下のような書き方が出来ることが分かりました。

if (document.myForm.elements['flag[]'][i].checked){
    count ++;
}

ソースはPHP-usersのログ(http://ns1.php.gr.jp/pipermail/php-users/2001-June/000215.html)です。やはり、JavaScriptとPHPで同じことを悩んでいた先人がいたのですね。しみじみ。


書き直したソースは以下の通りです。コメントやHTML、PHPの出力部分なども少し修正しております。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JavaScriptで複数のcheckboxの状態をチェックし、PHPで表示するサンプル</title>
<script language="javascript">

</script>
</head>
<body>
	<form name="myForm" action="<?php echo htmlspecialchars($_SERVER[’PHP_SELF’]);?>" method="POST">
		<label for="a">a : </label><input type="checkbox" name="flag[]" value="a" id="a">
		<label for="b">b : </label><input type="checkbox" name="flag[]" value="b" id="b">
		<label for="c">c : </label><input type="checkbox" name="flag[]" value="c" id="c">
		<label for="d">d : </label><input type="checkbox" name="flag[]" value="d" id="d">
		<label for="e">e : </label><input type="checkbox" name="flag[]" value="e" id="e">
		<input type="submit" value="check" onClick="arraySizeCheck()">
	</form>
<?php
	if ($_POST) {
		$flag = $_POST['flag'];
		foreach ($flag as $value) {
			print "<li>" . $value . " がチェックされました。\n";
		}
	}
?>
</body>
</html>

submitボタンのonClickにJavaScriptの関数を割り当ててますが、本当はonSubmitの方が良かったのかしら。JavaScriptでのチェックで1つもチェックされていない場合、PHPでの処理もさせずに終了、とかしたいんでしょうけど、上のサンプルだと無理ですよね。結局、PHPの処理部分で$_POSTが空の場合は何も出力しない、という形での実装になってしまっています。まぁ、その辺りはまた後で考えてみる、ということで。

それはそうと1つのファイル内にJavaScriptとPHPのコードを書いてると、単純な変数の書き方も怪しくなってくるから不思議。$なしで変数呼び出そうとしちゃったりして。やっぱりJavaScriptは別ファイルにした方が良いな、という当たり前のことを初めて身をもって実感したbonlifeことジュンイチでした。