riina-k.net

音楽・小説・プログラミングを手がけるリイナの拠点。

【Keyアレンジ】 Trampoline Girl (More Higher)

先日開催されたオンラインKeyアレンジイベント Thanks, Key 3 に投稿した1曲目です。

原曲はつい先日PC全年齢対象版も発売された「クドわふたー」より氷室先輩のテーマ「Trampoline Girl」。アレンジアルバムでボーカル化も果たした名曲です。
破天荒な行動に定評のある氷室先輩ですが、私のアレンジも破天荒で何をし始めるか分かりません

試聴は以下のリンクからよろしくです。
Thanks, Key 3 内のページ(mp3, 128kbps)
rk.tv 内のページ(mp3, 96kbps)

続きを読む

Thanks, Key 3 に2曲投稿しました!

Thanks, Key 3

Keyアレンジのオンラインイベント「Thanks, Key 3」に今回も2曲投稿しました。いまのところ皆勤!

1曲目:クドわふたー「Trampoline Girl」
2曲目:Angel Beats!「theme of SSS」 + planetarian「Gentle Jena」

楽曲の詳しい情報は公開され次第追記しようと思います。

2013/07/02追記
楽曲が公開されたので、個別記事を作成しました。
1曲目:「Trampoline Girl (More Higher)」
2曲目:「ハリウッド・デカはこうしてお星様になったのであった」

docomo GALAXY SIII α にMNP → 即root化 → IIJmioで運用してるGALAXY NoteとSIMを入れ替えてみた

auを裏切って突然のMNP!!

GALAXY S III α 最高です。空きメモリ普通に1ギガ超えてサクサク。
でもやっぱりroot取らないと遊び込めないよね!
ってことで。

GALAXY S III α と GALAXY Note のroot取った
これでAndroid端末5台のうちrootを取ってないのはEVO3Dだけとなりました。

ここでひとつ、以前からの疑問を解消すべく、両者のSIMを差し替える実験。
「GALAXY Note がLTEを掴まないのは回線の問題か端末の問題か」

まず、microSIMを抜くのに苦労した。コツを掴んでしまえば、爪先でSIMの端っこを片側ずつ押して、あとは上から指で押さえて小刻みに左右に動かしながら押し抜く感じ。

GALAXY S III α にIIJmioのSIM差して「やべえ通信できない!」ってなってたら単純にAPNの設定忘れてただけでした。
APN: iijmio.jp
ユーザー名: mio@iij
パスワード: iij
認証タイプ: PAPまたはCHAP
お馴染みのコレを設定したら……LTE掴んだ!!

一方、docomo純正SIMを差した GALAXY Note のほうは、APNの設定画面に入ろうとすると落ちる。
こりゃ完全に端末側の問題っすね。root取ったばっかりだけどファクトリーリセットかけてみますか。

ちなみに GALAXY S III α はIIJmio公式の動作確認バージョンが「SC03EOMAMA6」となってますが、最新の「SC03EOMAMC3」でもちゃんと動作するようです。
https://www.iijmio.jp/guide/outline/hdd/devices.jsp

カテゴリごとにTwitterウィジェットを切り替えるプラグイン作ってみた

ごぶさたーーーー!

ここ最近、TwitterクライアントとかRuby on RailsとかWordPressとかいろんなものを触る機会が増えた。
触るだけで成果が見えないのももったいないので、軽くWordPressプラグインを作ってみた。

TwitterPerCategory

名前のまんま。
私のようにジャンルごとにTwitterアカウントを使い分けてる人が多いか少ないかは分からないけど、
私の場合
 プログラミング → @Riina_Kw_pg
 音楽 → @Riina_Kw_m
 小説 → @Riina_Kw_nv
と、3ジャンルに分けてて、このWordPressも同様のカテゴリ分けをしてる。

ここで困ったことが。
世に出回ってるTwitterプラグインは複垢に対応してない!

てことで、自分で作ってみた。(地産地消)

現時点では「とりあえず公式ウィジェットの貼り付け」だけだけど、いずれはカテゴリごとのデザイン変更とか該当カテゴリへの投稿時にTwitterに自動投稿とかも実装したい。

05/09 追記:
データの持ち方にバグがあるようで、いまウィジェットが表示されてません。もう一度調べなおしてみます。

Javascript の prototype を学び始めた

既に存在するオブジェクトの振る舞いを変えてしまう prototype。
どう上手く扱えばいいのか、少しずつ勉強を始めてみた。

まず書いてみたのが、textarea から入力文字列を受け取る際、改行コードを統一する関数。

String.prototype.replaceAll = function (org, dest)
{
	return this.split(org).join(dest);
}

String.prototype.uniformLF = function()
{
	return this.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
}

正規表現を必要としない簡単な全置換が定義されていないため、replaceAll 関数を定義したのち、それを利用して改行コードを全置換。

これがうまく行ったので、以前の記事個人的に「これは組込み関数として必要だろ」っていうjavascript関数で作った関数を改造してみた。

Object.prototype.merge = function(arg)
{
	for ( var idx in arg ) {
		if ( arg.hasOwnProperty(idx) && typeof arg[idx] != 'undefined' && typeof arg[idx] != 'function' ) {
			this[idx] = arg[idx];
		}
	}
	return this;
}

Object.prototype.sliceKeys = function(keys)
{
	var result = {};
	for ( var idx in keys ) {
		var key = keys[idx];
		var item = this[key];
		if ( typeof item != 'undefined' && typeof item != 'function' ) {
			result[key] = ( this.hasOwnProperty(key) ? item : null );
		}
	}
	return result;
}

Object.prototype.walk = function(cb, param)
{
	if ( !cb ) {
		return;
	}
	for ( var idx in this ) {
		if ( this.hasOwnProperty(idx) ) {
			var item = this[idx];
			if ( typeof item != 'undefined' && typeof item != 'function' ) {
				cb(item, idx, param);
			}
		}
	}
}

for( … in …… ) の構文だと、プロパティやメソッド、prototype で指定したメソッドまで現れてしまうので、this.hasOwnProperty() 関数を多用するハメに。

しかしこれのおかげで、前の記事で書いたサンプルコードがこんな感じに豹変。

// 元のデータを保持するオブジェクトを生成
var obj = {
	id: 123,
	title: 'rk.tv',
	link: [
		'http://riina-k.net/',
		'http://riina-k.tv'
	]
};

function printHTML(item, key, jq)
{
	alert( '<dt>' + key + '</dt>' + '<dd>' + item + '</dd>' );
}
$('body').append('<dl id="output"/>');
	
// そこにプロパティを追加、切り出したプロパティそれぞれにコールバック関数を適用し、dlタグを出力
obj.merge({ author: 'Riina K.', param: 'foooo' }).sliceKeys(['title', 'author']).walk( printHTML, $('dl#output') );

追加したメソッドの戻り値のおかげで、メソッドチェーンを使って複雑な処理も1行で書けるようになった! これは便利。

遅延ロードのライブラリをクロスブラウザ対応

先日の記事「遅延ロードをライブラリ化してみた」がIEで動作しなかった原因が分かったので、一部書き直し。

delayload.js

変更点は2箇所。ひとつは「オブジェクトのプロパティとして class という名前は使用できない」という制約があるため、これを「target」というプロパティ名に変更。
もう一つは、img要素を生成する際、先にwidth, heightを設定してからsrcを設定してしまうと、IEでは先に設定したサイズを無視してオリジナルサイズでwidth, heightを上書きしてしまうため、srcを設定してからwidth, heightを設定という順番に変更。

var DelayLoad = {
	target: "delay",
	error: "error",
	retry: 1,
	init: function(params)
	{
		if ( params && params.hasOwnProperty("target") ) {
			this.target = params.target;
		}
		if ( params && params.hasOwnProperty("retry") ) {
			this.retry = params.retry;
		}
		if ( params && params.hasOwnProperty("error") ) {
			this.error = params.error;
		}
		
		// 対象のjQueryオブジェクトにパラメータを設定
		var jqTarget = jQuery( "*[class^='" + this.target + "']" );
		for ( i=0; i<jqTarget.length; ++i ) {
			jQuery.data( jqTarget.get(i), "retry", this.retry );
			jQuery.data( jqTarget.get(i), "error", this.error );
		}
		jqTarget.each(function(){
			// 引数をパース
			var paramsString = this.className.split(";")[1];
			var arrParams = paramsString.match(/^params=\{(.*?)\}$/)[1].match(/([a-z]+):('(.*?)'|[0-9]+)/g);
			var objParams = {};
			for ( i=0; i<arrParams.length; ++i ) {
				var tmp = arrParams[i].split(":");
				if ( tmp[1].match(/^'(.*?)'$/) ) {
					objParams[ tmp[0] ] = RegExp.$1;
				} else {
					objParams[ tmp[0] ] = tmp[1];
				}
			}
			if ( !objParams.hasOwnProperty("url") ) {
				throw "DelayLoad: url required";
			}
			if ( !objParams.hasOwnProperty("title") ) {
				objParams.title = "";
			}
			if ( !objParams.hasOwnProperty("retry") ) {
				objParams.retry = jQuery.data( this, "retry" );
			}
			objParams.error = jQuery.data( this, "error" );
			
			var jqImg = jQuery("<img />");
			// エラー時のイベントハンドラ
			jqImg.bind("error", function(e){
				// 再試行回数だけリロード
				var retry = jQuery.data( this, "retry" );
				if ( --retry > 0 ) {
					jQuery.data( this, "retry", retry );
					this.src = this.src;
				} else {
					// 試行回数切れ
					var target = jQuery.data( this, "target" );
					var error = jQuery.data( this, "error" );
					target.replaceWith(error);
					// 後始末
					jQuery.removeData( this, "retry" );
					jQuery.removeData( this, "error" );
					jQuery.removeData( this, "target" );
				}
			});
			// ロード時のイベントハンドラ
			jqImg.bind("load", function(e){
				var target = jQuery.data( this, "target" );
				target.replaceWith( jQuery(this) );
				// 後始末
				jQuery.removeData( this, "retry" );
				jQuery.removeData( this, "error" );
				jQuery.removeData( this, "target" );
			});
			// 再試行回数を設定
			jQuery.data( jqImg.get(0), "retry", objParams.retry );
			// エラーHTMLを設定
			jQuery.data( jqImg.get(0), "error", objParams.error );
			// 画像置換対象
			jQuery.data( jqImg.get(0), "target", jQuery(this) );
			// 読み込み
			jqImg.attr("src", objParams.url);
			if ( objParams.hasOwnProperty("width") ) {
				jqImg.attr("width", objParams.width);
			}
			if ( objParams.hasOwnProperty("height") ) {
				jqImg.attr("height", objParams.height);
			}
			if ( objParams.hasOwnProperty("title") ) {
				jqImg.attr("alt", objParams.title);
			}
		});
	}
};

DelayLoad デモ

現時点では、以下のブラウザで動作を確認。

  • [Windows XP] IE 8.0
  • [Windows XP] Firefox 13.0.1
  • [Windows XP] Opera 11.64
  • [Windows 7] Opera 23.0

クロスブラウザ対応がとりあえずできたということで、今回をもって「DelayLoad ver 1.0」としますか。
前回書いたものはβ版ってことで。

デモファイルは http://riina-k.net/delayload/1.0/ 以下にあるので、欲しい人はソース漁ってください 要望があればzipにまとめてDLできるようにしますかねー。

遅延ロードをライブラリ化してみた

先ほどのエントリー「画像ロード時のエラー制御で試行錯誤」で組んだjavascript、エラー制御というより「読み込み中」的な意味で使い回しができそうなので汎用性を持たせてライブラリ化。

delayload.js

DelayLoad = {
	class: "delay",
	error: "error",
	retry: 1,
	init: function(params)
	{
		if ( params && params.hasOwnProperty("class") ) {
			this.class = params.class;
		}
		if ( params && params.hasOwnProperty("retry") ) {
			this.retry = params.retry;
		}
		if ( params && params.hasOwnProperty("error") ) {
			this.error = params.error;
		}
		
		// 対象のjQueryオブジェクトにパラメータを設定
		var jqTarget = jQuery( "*[class^='" + this.class + "']" );
		for ( i=0; i<jqTarget.length; ++i ) {
			jQuery.data( jqTarget.get(i), "retry", this.retry );
			jQuery.data( jqTarget.get(i), "error", this.error );
		}
		jqTarget.each(function(){
			// 引数をパース
			var paramsString = this.className.split(";")[1];
			var arrParams = paramsString.match(/^params=\{(.*?)\}$/)[1].match(/([a-z]+):('(.*?)'|[0-9]+)/g);
			var objParams = {};
			for ( i=0; i<arrParams.length; ++i ) {
				var tmp = arrParams[i].split(":");
				if ( tmp[1].match(/^'(.*?)'$/) ) {
					objParams[ tmp[0] ] = RegExp.$1;
				} else {
					objParams[ tmp[0] ] = tmp[1];
				}
			}
			if ( !objParams.hasOwnProperty("url") ) {
				throw "DelayLoad: url required";
			}
			if ( !objParams.hasOwnProperty("title") ) {
				objParams.title = "";
			}
			if ( !objParams.hasOwnProperty("retry") ) {
				objParams.retry = jQuery.data( this, "retry" );
			}
			objParams.error = jQuery.data( this, "error" );
			
			var jqImg = jQuery("<img />");
			// エラー時のイベントハンドラ
			jqImg.bind("error", function(e){
				// 再試行回数だけリロード
				var retry = jQuery.data( this, "retry" );
				if ( --retry > 0 ) {
					jQuery.data( this, "retry", retry );
					this.src = this.src;
				} else {
					// 試行回数切れ
					var target = jQuery.data( this, "target" );
					var error = jQuery.data( this, "error" );
					target.replaceWith(error);
					// 後始末
					jQuery.removeData( this, "retry" );
					jQuery.removeData( this, "error" );
					jQuery.removeData( this, "target" );
				}
			});
			// ロード時のイベントハンドラ
			jqImg.bind("load", function(e){
				var target = jQuery.data( this, "target" );
				target.replaceWith( jQuery(this) );
				// 後始末
				jQuery.removeData( this, "retry" );
				jQuery.removeData( this, "error" );
				jQuery.removeData( this, "target" );
			});
			// 再試行回数を設定
			jQuery.data( jqImg.get(0), "retry", objParams.retry );
			// エラーHTMLを設定
			jQuery.data( jqImg.get(0), "error", objParams.error );
			// 画像置換対象
			jQuery.data( jqImg.get(0), "target", jQuery(this) );
			// 読み込み
			if ( objParams.hasOwnProperty("width") ) {
				jqImg.attr("width", objParams.width);
			}
			if ( objParams.hasOwnProperty("height") ) {
				jqImg.attr("height", objParams.height);
			}
			if ( objParams.hasOwnProperty("title") ) {
				jqImg.attr("alt", objParams.title);
			}
			jqImg.attr("src", objParams.url);
		});
	}
};

使い方はこんな感じ。

<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/delayload.js"></script>
<script type="text/javascript">
	jQuery(function(){
		DelayLoad.init({
			error: "<strong style='color:red;'>読み込みに失敗しました</strong>"
		});
	});
</script>
<body>
	<strong>画像直リン</strong>
	<img class="delay;params={url:'images/ongakuji.jpg',width:480,height:270,title:'音楽寺'}" src="images/loading.gif" width="16" height="16" />
	<strong>意図的に2秒待たせるPHP</strong>
	<img class="delay;params={url:'wait2seconds.php',width:480,height:270,title:'音楽寺(2秒遅れ)'}" src="images/loading.gif" width="16" height="16" />
	<strong>存在しないリンクを3回再試行</strong>
	<img class="delay;params={url:'no_longer_exists',width:480,height:270,title:'存在しないURL',retry:3}" src="images/loading.gif" width="16" height="16" />
</body>

実行結果はこんな感じ。DelayLoad デモ

軽く解説。
あらかじめjQueryがロードされていることが条件。
DOMのロードが終わった状態で、DelayLoad.init()関数を実行する。
このとき引数として置換対象クラスclass、エラー時に表示されるHTMLerror、再試行回数retryをパラメータとするオブジェクトを渡すことができる。
値を省略した場合、対象クラスは「delay」、エラーメッセージは「error」、再試行回数は1回。

DelayLoad.init({class:"mydelay",retry:3});

HTMLでは、置換パラメータをクラス指定の中に記述する。
最終的にはクラス指定した要素が画像で置き換わるので、クラス指定をするのはimgタグである必要はない。
パラメータの指定方法は 「init関数で指定したクラス名;params={……}」、前記のとおり、クラス名のデフォルトは「delay」。
paramsにはいくつかのパラメータを書くことができる。
url : 必須、読み込む画像のURL。
width : 画像を表示する横幅、省略した場合はimgタグでwidthを書かなかったときと同じ動作。
height : 画像を表示する縦幅、省略した場合はimgタグでheightを書かなかったときと同じ動作。
title : 画像のaltに設定される画像のタイトル。省略した場合はaltが出力されない。
retry : エラー時の再試行回数。init関数での指定よりも優先される。省略した場合はinit関数の設定またはデフォルト値。

<img class="delay;params={url:'images/ongakuji.jpg',width:480,height:270,title:'音楽寺'}" src="material/loading.gif" width="16" height="16" />

現時点ではIEには対応してません。
要望があればバージョンアップしたいなー。

画像ロード時のエラー制御で試行錯誤

開発中のCDデータベースを試しにこのサーバにアップロードしたところ、
開発環境であるローカルホストでは発生しなかった「画像へのリクエストで500エラー」という状況が発生。

実はCDのジャケット画像をサーバ内部の見えないところでキャッシュしておいて、パラメータで出力する画像を選択、キャッシュが存在すればそのまま出力、存在しなければ404を返すというPHPを組んでいた。

404エラーと500エラーの双方に対応するため、まずAjaxで500エラーを捕捉するよう、次のように書いてみた。

PHP

まずはAjaxでジャケット画像の縦横サイズを取得できるよう、独自ヘッダでサイズを出力。

header( 'Content-type: ' . $this->mime );
header( 'Content-Length: ' . $this->bytes );
// 独自ヘッダ
header( 'X-Width: ' . $this->width );
header( 'X-Height: ' . $this->height );

HTML

ジャケット画像が正常に読み込まれるまではローディング中を示す例のくるくる回るやつを表示しておいて、alt属性に読み込みたいURLを書いておく。
※ {{ …… }} はSmartyのタグ、$pkgはパッケージ情報のインスタンス、$imgは画像情報のインスタンス。

<tr id="{{$pkg->htmlid|escape}}">
	<td class="jacket">
		<img src="material/loading.gif" width="16" height="16" alt="{{$img->pathWeb|escape}}" title="{{$title_artist}}" />
	</td>
</tr>

javascript

コールバック関数内で、altに記述されたURLを読み込み、ステータスが200になったらsrcにジャケット画像のURLを設定。

function loadJacketImage(jqImage, count)
{
	$.ajax(
		{
			jq: jqImage,	// 対象となるimgタグのjQueryオブジェクト
			count: count,	// 再試行回数
			url: jqImage.attr("alt"),
			success: function(data, status, xhr){
				var width = xhr.getResponseHeader("X-Width");
				var height = xhr.getResponseHeader("X-Height");
				this.jq.attr("src", this.url).attr("alt", "").attr("width", width).attr("height", height);
			},
			error: function(xhr, status, error){
				if ( xhr.status == 404 ) {
					this.jq.replaceWith("no image");
				} else {
					if ( this.count > 0 ) {
						// 再試行
						loadJacketImage(this.jq, this.count-1);
					} else {
						this.jq.replaceWith("error");
					}
				}
			}
		}
	);
}

$(function(){
	$(".jacket img").each(function(){
		loadJacketImage( $(this), 3 );
	});
});

しかしこの方法には大きな罠があり、「Ajaxで取得できた画像のURLをsrcに設定する」タイミングでもう一度リクエストが飛んでしまい、そのときに500エラーとなってしまうという状況が時々発生してしまう。
キャッシュしてくれないんかい……

そこでイベントハンドラを利用した以下のような方法を試してみた。

HTML

<tr id="{{$pkg->htmlid|escape}}">
	<td class="jacket">
		<img src="{{$img->pathWeb|escape}}" width="{{$img->width|escape}}" height="{{$img->height|escape}}" alt="{{$title_artist}}" />
	</td>
</tr>

javascript

img要素のイベントハンドラで、エラー時に単純にリロードさせるためにsrcを上書き。

$(function(){
	$(".jacket img").bind("error", function(e){
		// リロード
		this.src = this.src;
	});
});

今度はイベントハンドラで404と500の区別が付かない。
そもそもPHPのほうで404が返ってくるようなURLは最初から書かないように変更し、500エラーが返ってきてもリロードが働いて正常に表示されるようになった。
ただ、PHPが何らかの影響でエラーを吐き続けた場合、リロードが無限ループしてしまうので、Ajaxの時と同様に再試行回数を設定できるようにしたい。

あとやっぱり例のくるくる欲しいよね。
ってことでいじくり回して、最終的にこんな感じ。

HTML

ジャケット画像が存在しない場合はあらかじめ「no image」を表示、存在する場合は例のくるくるに対してalt指定でジャケット画像のURL、横幅、高さをカンマ区切りで設定。

<tr id="{{$pkg->htmlid|escape}}">
	<td class="jacket">{{strip}}
		{{if $img->exists }}
			<img src="material/loading.gif" width="16" height="16" alt="{{$img->pathWeb|escape}},{{$img->width|escape}},{{$img->height|escape}}" title="{{$title_artist}}" />
		{{else}}
			no image
		{{/if}}
	{{/strip}}</td>
</tr>

javascript

成功時に例のくるくるをジャケット画像で置き換え、エラー時には指定回数だけリロードを試み、一定回数(このサンプルの場合は3回)を超えると例のくるくるを「error」という文字列で置き換え。
コールバック関数内で置換対象のくるくると試行回数を見失ってしまうと意味が無いので、jQueryの内部APIで要素に関連付けておく。コールバック関数に引数を渡す方法がめっちゃめんどくさいの

$(function(){
	$(".jacket img").each(function(){
		var tmp = this.alt.split(",");
		var jqImg = $("<img />");
		
		// エラー時のイベントハンドラ
		jqImg.bind("error", function(e){
			// 再試行回数だけリロード
			var tryCount = jQuery.data( this, "tryCount" );
			if ( --tryCount > 0 ) {
				jQuery.data( this, "tryCount", tryCount );
				this.src = this.src;
			} else {
				// 試行回数切れ
				var id = jQuery.data( this, "target" );
				$("#" + id + " img").replaceWith("error");
			}
		});
		// ロード時のイベントハンドラ
		jqImg.bind("load", function(e){
			var id = jQuery.data( this, "target" );
			$("#" + id + " img").replaceWith( $(this) );
			// 後始末
			jQuery.removeData( this, "tryCount" );
			jQuery.removeData( this, "target" );
		});
		
		jQuery.data( jqImg.get(0), "tryCount", 3 );								// 再試行回数を設定
		jQuery.data( jqImg.get(0), "target", $(this).parents("tr").get(0).id );	// 画像置換対象のID
		jqImg.attr("src", tmp[0]).attr("width", tmp[1]).attr("height", tmp[2]).attr("alt", $(this).attr("title"));
	});
});

ここまでやってようやく気が済んだのでした。
そもそもPHPが500を返すほどサーバに負荷をかけてる事実をなんとかするべきじゃ?

個人的に「これは組込み関数として必要だろ」っていうjavascript関数

// オブジェクトの結合
function merge()
{
	var result = {};
	for( var i=0; i<arguments.length; i++ ){
		var arg = arguments[i];
		for ( var itm in arg ) {
			if ( arg.hasOwnProperty(itm) ) {
				result[itm] = arg[itm];
			}
		}
	}
	return result;
}

// 配列 keys に存在する要素をキーとする値だけをオブジェクト obj から取り出す
function slice(obj, keys)
{
	var result = {};
	for ( var idx in keys ) {
		key = keys[idx];
		result[key] = ( obj.hasOwnProperty(key) ? obj[key] : null );
	}
	return result;
}

// PHPの array_walk() のクローン
function walk(obj, callback, param)
{
	for ( var idx in obj ) {
		var item = obj[idx];
		if ( typeof item != 'undefined' && typeof item != 'function' ) {
			callback(item, idx, param);
		}
	}
}

この3つ。
merge関数は「objectをマージする – javascript:humming bird」を参考に。
slice関数については関数名が適切じゃないかもしれない。
(本来 slice っていうと、配列のi番目からj番目までを切り出すっていう意味っぽいから)

この merge, slice, walk を組み合わせると、こんなことができる。(一応前提としてjQueryを使用、ほかのライブラリでも同様の記述はできるはず)

$(function(){
	// 元のデータを保持するオブジェクトを生成
	var obj = {
		id: 123,
		title: 'rk.tv',
		link: [
			'http://riina-k.net/',
			'http://riina-k.tv'
		]
	};
	// そこにプロパティを追加
	obj = merge( obj, { author: 'Riina K.', param: 'foooo' } );

	// さらに必要なプロパティのみを切り出す
	var target = slice( obj, ['title', 'author'] );

	function printHTML(item, key, jq)
	{
		jq.append( '<dt>' + key + '</dt>' + '<dd>' + item + '</dd>' );
	}

	// 切り出したプロパティそれぞれにコールバック関数を適用し、dlタグを出力
	$('body').append('<dl id="output"/>');
	walk( target, printHTML, $('dl#output') );
});

実行結果

<dl id="output"><dt>title</dt><dd>rk.tv</dd><dt>author</dt><dd>Riina K.</dd></dl>

オブジェクトやコールバック関数を多用するjavascriptだからこそ、こんな関数たちが居てくれると便利。

return top