]> Bookmarklets for Nintendo DSi Browser

ニンテンドーDSiブラウザー 用ブックマークレット

DSiブラウザー (Opera 9.50) で便利に使える ブックマークレット集です。

◆追加用補助リンクの使い方

追加用補助リンクを各項目に置いてあります。 クリックするとエラーが出ますが、かまわず ★お気に入りに「ページを追加」し、 「編集」で頭の opera:を削ってください。 タイトルも各自で勝手につけてください。 DSiブラウザーではPC用のように リンクをブックマークバーへドラッグで追加、 みたいな器用な操作ができないため、こうしています。

$Id: bookmarklet.html,v 1.71 2016-06-05 23:57:36+09 kabe Exp $


ベタテキストで書かれたURLにジャンプする (jump to selected URL in text)

DSiブラウザーはコピーペーストができないので、 <A HREF> になっていないベタ書きURLに簡単にアクセスできません。

テキスト部分をタッチペンで長押しすると、文字列を領域選択できます。 普通はこれを検索エンジンに食わせて検索するんですが、

以下のようなURL (Bookmarklet)を「お気に入り」の「編集」で入れておけば、 ベタ書きURLをテキスト選択後にその お気に入りを選択することで そのURLに飛べるようになります。

javascript:function(w,u){w.location=(u=w.getSelection()+'').indexOf('://')<0?'http://'+u:u;}(window);
[お気に入りの編集 画面]

追加用補助リンク


リンク先のアドレス(URL)を確認する (Show URL of a link)

DSiブラウザにはステータスバーがないので、<A href>のリンク先が どんなURLかを簡単に確認できません。

以下のようなURL (Bookmarklet)を「お気に入り」の「編集」で入れておけば、 リンクを選択 ( /L◘] もしくは [◘R\ ボタンを押しながら十字キー) 後に その お気に入りを選択することで そのURLを確認できます。

javascript:alert(document.activeElement.href)


追加補助リンク

リンク先のアドレスを編集してからジャンプ (Edit URL of a link, and jump)

リンク先のアドレス(URL)を確認のうえ、 ちょっと編集した後にそこへ飛ぶ、といった操作に使います。 PCブラウザなら、 リンクを右クリック→「リンクのコピー」→アドレスバーに貼り付け→編集→Enter、 としているような操作を可能にします。

<A href> だけでなく、 ベタテキストをタッチペン長押しで選択した文字列や、 フォーム部品 (textboxやSubmitボタン) のリンク先も 一応表示・編集できます。 onclickも拾えるものは表示。

サンプル画面:
[リンクURLの編集 サンプル画面]

javascript:
## Show HREF of the selected element in TEXTAREA.

## Selected document.activeElement could be
## - <A>
## - <FORM><INPUT>
## - <LI><A><block>
function(d,h){
function U(s){return s.toUpperCase()}
var a=d.activeElement,p=a.parentNode;
if(!h){
  //h is getSelection(). if null, try activeElement
  h=a.href;
  if(U(a.nodeName)!='A'&&U(p.nodeName)=='A'){
	## When <LI><A href><block></A></LI>, or <LI onclick>, 
	## <block> will be the activeElement. Use parent's href.
	h=p.href;
  }
}
if(!h&&a.form){
	## <FORM><INPUT>
	## try use <FORM action="..."> as the URL
	h=a.form.action;
	for(var q=0;q<a.form.length;q++){h+=(q?'&':'?')+a.form[q].name+'='+a.form[q].value;}     //build full QUERY_STRING
}
## Try getting onclick string.
## Only explicit <INPUT onclick="<code>"> is extractable via getAttribute();
## dynamically set hooks are only .toString()="[ECMA script code]"
var c;
for(p=a;p;p=p.parentNode){  //iterate up the hierarchy for onclick hook
	if('function'==typeof p.onclick){
		c=p.getAttribute('onclick');
		if(!c)c=p.onclick+''; //try toString() method anyway
		break;
	}
}
//'?';  // prevent \ to be / below
//function e(s){return s.replace(/[&<>"]/g,function(c){return '\&amp;#'+c.charCodeAt(0)+';'})}
d.write('<SCRIPT>
function $(id){return document.getElementById(id);}
</SCRIPT>'+
(a.outerHTML?a.outerHTML:'')+'<br>
<FORM>
<TEXTAREA cols=48 rows=5 name=8 id=8>'+h.replace(/[&<>]/g,function(c){return '\&#'+c.charCodeAt(0)+';'})+'
</TEXTAREA><br>
<INPUT type=button name="goto" value="Jump as URL" onclick="location.href=$(8).value" />
<INPUT type=button name="unescape" value="unescape" onclick="$(8).textContent=$(8).value=decodeURIComponent($(8).value)" />'+
(c?'<br>onclick:'+c:'')+'
</FORM>
')
}(document,getSelection()+'');

追加補助リンク

[unescape]ボタンで、URL中の %E6%A4%9C といった エンコード文字列をベタ文字列に戻します。UTF-8のみ対応。 中身を確認したい用途です。URLとしてジャンプは不可能になることが多いです。


今見ているページを Google ケータイ変換にかける

今見ているページが重すぎて途中でレンダリングが止まる場合は、 Google Wireless Transcoder 経由にすれば見れる可能性があります。

以下のようなURL (Bookmarklet)を「お気に入り」の「編集」で入れておけば、 現在のページをGWT経由で表示できます。 サイトによってはアクセス拒否したりリダイレクトされたりと、必ずしもうまくいく わけではないようですが。

javascript:window.location.href='http://www.google.co.jp/gwt/x?gl=JP&hl=ja-JP&oe=sjis&u='+escape(window.location.href);

追加用補助リンク

今見ているページを Google transcode 変換にかける

軽量変換には googleweblite もあります。 こっちのほうが軽量です。 Cookieは消されます。

javascript:window.location.href='http://googleweblight.com/?f=1&lite_url='+escape(window.location.href);

追加用補助リンク

transcode されたHTMLでは <meta content="width=device-width"> でタテ長表示が強制されます。 iPhone用しか考えてない典型例ですね。 User-Agent も Mobile Safari になります。

DSiブラウザでは字がかなり大きくなってしまうので、 タテ長表示を解除 ブックマークレットと 併用したほうが良いかもしれません。


今見ているページを はてなモバイルゲートウェイにかける

javascript:window.location.href='http://mgw.hatena.ne.jp/?split=0&url='+encodeURIComponent(location.href);
追加用補助リンク

ケータイ向けだけあり、はてなモバイルゲートウェイのほうが軽いです。 cookieも削除されます。ちょっとした広告がつくのと、 &lt; &gt; の処理が変な気がしますが。


画像をリンクへ変換する (Convert Image to a Link to the image)

普段は画像の読み込み オフ で使っている人用のブックマークレットです。 [画像]を、画像へのリンクに変換します。 ちょっとその画像だけ見たい、ときに重宝します。 (画像オフでも直接画像へのリンクを踏めば画像だけは見れます)

[altすらなく意味不明なページ]
使用前
[とりあえずファイル名だけついた]
使用後
追加用補助リンク
※注:上のコードはそのままでは動きません。補助リンクを使ってください。

画像が他所へのリンクやsubmitボタンや余計なonclickつきだったなど、 元の機能は失われるので、戻す際はリロードしてください。

DSiブラウザーでは右クリック→画像を読み込み、のような操作ができないので、 こういうブックマークレットが必要です。

DSiブラウザーを初期化した際に、お気に入りの最初に入っている 「ニンテンドーDSi リンク集」は、画像だけで alt も何も ついていないので、画像オフにしていると全く意味不明です。 なお、このページはDSiブラウザー以外はホームページに飛ばされるよう 細工されているので、PCなどで見る際は すぐ読み込み停止させるか、 いったんダウンロードしてからエディタで編集してリダイレクトロジックを潰します。

欧州用DSiリンク集は ちゃんとしたaltがついてるし、 北米用DSiリンク集では DSi以外を締め出すこともしてないんですけど…


強制的にタテ長表示に切り替える (force vertical mode/set viewport to arbitrary value)

小型端末のブラウザは <META name=viewport> を設定することで 表示のされ方を変更できます。

DSiブラウザーでは content="width=240" もしくは width=device-width を指定することで、タテ長表示モードに強制的に変更できます。 opera:about なんかでは画面モードの 変更ができないようになっていますが、それと同じです。

javascript:function(){var w=prompt('Set viewport to (768/240/device-width)','width=device-width');if(w)document.getElementsByTagName('head')[0].innerHTML+='<meta name="viewport" content="'+w+'" />';}()

追加用補助リンク

副作用で、タテ長表示にしていてもスタイルシートが完全に有効になります
が、小型端末用のスタイルシートを準備しているサイトは ほとんどないので、大抵は見るも無残な状態になります。

要は2画面モードの拡大画面を無理やり240pxに狭めた状態になる。 横幅800px決めうちでレイアウトしているサイトがちゃんとレンダリング できるはずがないので、 一般サイトでは笑いを取る以外の実用性はない、かも。

逆に、DS用サイトを謳っていたり、モバイル対応を自称しつつ iPhone専用サイトになっていてちゃんと見れないサイトで、 強制されたタテ長表示を解除する、という用途には 実用性があるかもしれません。

ブックマークレットを起動するとprompt()で設定値を 聞かれますが、そのまま「決定」すれば強制 タテ長表示 になります。 width=768を指定すれば、2画面モードのデフォルト値になります。

DSiブラウザーは CSSの @media typeMedia Queries に 対応していて、しかるべき CSS を準備してやれば 同じページでPC用と小型端末用の両対応ができることになってます。 width切替テストのページ を作ってみましたので、つつきまわしてみてください。 (Opera 9.5といえど完璧でもないことがわかる)


強制されたタテ長表示を解除する (disengage the forced vertical mode)

[呂 通常表示にする] iPhoneで見ることしか考えていないサイトなど、 強制的にタテ長表示モードにされているのを解除して 通常表示にできるようにします。 タテ長にするよりは使う頻度は高そうです。

タテ長表示を常用しているのであればSSRモードが有効になるので、 iPhone専用サイトではより見やすくなります。

javascript:function(){document.getElementsByTagName('head')[0].innerHTML+='<meta name="viewport" content="width=768" />'}()

追加用補助リンク

今見ているページの Cookie を見る (View Cookie)

Cookieを見るブックマークレットは色々ありますが、 見るだけなら alert()を使うのが簡単でしょう。 動作も軽い。

javascript:alert(document.cookie);

追加用補助リンク

Cookieには name=value の他に ;path=/... ;domain=.domain ;expires=date といった属性がありますが、JavaScriptからはアクセスできません。 (なので、「Cookieを消す」ブックマークレットは色々苦労している模様)


今見ているページの Cookie をすべて消す (Delete Cookie)

今見ているページのCookieを全部消します。 標準機能では「設定」→「Cookieの削除」での 全サイトの全消去しかできないので、 個別のサイト、あるいはウェブページの単位で消したい、 今消したい、 時に使います。

サンプル画面:
[Cookieの削除 サンプル画面]

javascript:
(function(_d,_l){
## Delete a cookie
function d(c){
	var h,
	    o=_d.cookie;
	c+='=;expires=Thu, 01-Jan-1970 00:00:00 GMT';
	function r(c){
		## Iterate up /path/to/here
		## "_d.cookie==o" checks if the cookie is actually deleted,
		## in incomplete attempt to delete only the 
		## cookie of same name with deeper path.
		## This check also significantly reduced exec time.
		for(var v=_l.pathname.split('/');_d.cookie==o&&v.length;v.pop()){
			## delete the cookie
			_d.cookie=c+';path='+(v.length==1?'/':v.join('/'));
		}
	}
	## Try erasing without ";domain"
	r(c);
	## Try erasing with ";domain=..."
	//detail.chiebukuro.yahoo.co.jp
	//      .chiebukuro.yahoo.co.jp
	//                 .yahoo.co.jp
	//                       .co.jp
	## "_d.cookie==o" also here to halt at longer domain
	for(h=_l.hostname;_d.cookie==o&&h.split('.').length>2;h=/[.]?[^.]*(.*)/.exec(h)[1]){
		r(c+';domain='+h);
	}
}
var i,cs=_d.cookie.split(/ *; */);
if(!cs||!cs[0]){ ## if no cookie, cs[0] will be ''
	alert('no cookies');return;
}
if(!confirm('Delete '+cs.length+' cookies?\n'+_d.cookie))return;
for(i in cs)d(cs[i]);
})(document,location);	## top wrapper
追加用補助リンク

制限: 今見ているサイトのCookieしか消せません。 これは JavaScript (というかDOMのdocument.cookie) の制限です。 PC用のブラウザのようにオフラインでCookieの閲覧編集は DSiブラウザではできません。

DSiブラウザーはCookie保持数に制限があるみたいなので、 2度と来ないようなページを見た際は こまめにCookieを消したほうがいいのかもしれません。


今見ているページの Cookie を見る、個別に消す (View and delete Cookie)

今見ているページのCookieの一覧を表示し、個別に[delete]ボタンで消します。

サンプル画面:
[Cookieの選択削除 サンプル画面]

javascript:
### Bookmarklet to show and selectively delete a Cookie of current page
document.write('<SCRIPT>
_dc=document;
## Read the current cookie, build a table, and 
## re-inject the HTML into _cKp element
function t(){
	function e(s){
	    ## we need to escape ' also to prevent Cookie-based attack
	    return s.replace(/[&<>`"]/g,function(c){return `&#`+c.charCodeAt(0)+`;`;})
	}
	var q="`",
	    h=`<TABLE border=1><CAPTION>Cookies on `+e(location.href)+`</CAPTION>`,
	    cs=_dc.cookie.split(`;`),
	    i,v;
	if(!cs||!cs[0]){ ## if no cookie, cs[0] will be ``
		## Showing nothing isn't nice, so say so
		cs=[`---=no cookies`];
	}
	## Build a table of |name|value|[delete]|
	for(i in cs){
		if(v=/ *([^=]*)=(.*)/.exec(cs[i])){
			h+=`<TR><TD>`+e(v[1])+`<TD><INPUT type=text value="`+e(v[2])+`"><TD><INPUT type=button value=delete onclick="javascript:this.disabled=true;d(`+q+e(v[1])+q+`);">`;
		}
	}
	## re-inject HTML
	_cKp.innerHTML=h+`</TABLE>`;
}
## Delete a cookie
function d(c){
	var h,
	    o=_dc.cookie;
	c+=`=;expires=Thu, 01-Jan-1970 00:00:00 GMT`;
	function r(c){
		## Iterate up /path/to/here
		## "_dc.cookie==o" checks if the cookie is actually deleted,
		## in incomplete attempt to delete only the 
		## cookie of same name with deeper path.
		## This check also significantly reduced exec time.
		for(var v=location.pathname.split(`/`);_dc.cookie==o&&v.length;v.pop()){
			## delete the cookie
			_dc.cookie=c+`;path=`+(v.length==1?`/`:v.join(`/`));
		}
	}
	## Try erasing without ";domain"
	r(c);
	## Try erasing with ";domain=..."
	//detail.chiebukuro.yahoo.co.jp
	//      .chiebukuro.yahoo.co.jp
	//                 .yahoo.co.jp
	//                       .co.jp
	## "_dc.cookie==o" also here to halt at longer domain
	for(h=location.hostname;_dc.cookie==o&&h.split(`.`).length>2;h=/[.]?[^.]*(.*)/.exec(h)[1]){
		r(c+`;domain=`+h);
	}
	t();	## regenerate table
}
_dc.write(`<P id=_cKp></P>`);
_cKp=_dc.getElementById(`_cKp`);
t();
</SCRIPT>'.replace(/`/g,"'")
);
追加用補助リンク
※注:上のコードはそのままでは動きません。補助リンクを使ってください。 (ECMAScriptでは本来、文字リテラル中では改行できない、など)

同名で ;domain=;path= が異なる Cookieが複数ある場合、 [delete] を押したほうでないCookieが消えることもあります
がそんなCookieの使い方をしているサイトは少ないので 実質問題はないでしょう。

DSiブラウザーのJavaScriptはエラーがあると何も言わずそこで 止まってしまうので、デバッグは難しかったです。 このブックマークレットは長いので、改造するにしても1000文字制限には注意。


今見ているページの Cookie を保存する (Save Cookie)

DSiブラウザではクッキーの削除は「設定」→「Cookieの削除」での 全消去しかできないので、 せめてCookieを使っていることが分かっているサイトのCookieだけでも 保存しておきたい、時に使います。

javascript:
## Bookmarklet to save cookie of the current site. 
## JavaScript cannot catch ";path=/...;expires=...", so
## the saved cookie will always be "path=/"(root path, session cookie)
##
document.write('<A href="opera:javascript:'+
	// inside href="PCDATA" region. No " allowed(should be &#34;)
	('(function(){
	if(location.host!=`'+location.host+'`){
		alert(`not for this site`);return
	}
	var c=['+
	// create ['NAME=VALUE','name=value',...], with each escape()ed
	function(d){
		c=d.cookie.split(/ *; */);
		if(!c||!c[0]){return ''}
		for(var i in c){
			c[i]="'"+escape(c[i])+"'"
		}
		return c.join(',')
	}(document)+
	'];
	if(confirm(`Restore following cookies?\\n`+c.join(`\\n`))){
		for(var i in c){
			document.cookie=unescape(c[i])+`;path=/`
		}
	}
	})();').replace(/`/g,"'")+
	'">Save cookie</A> &lt;&lt; Click,Bookmark,Edit and delete "Opera:"<br>'+
	c.length+' cookies for site '+location
)
追加用補助リンク
※注:上のコードはそのままでは動きません。補助リンクを使ってください。

◇使い方

Cookieの保存
Cookieの復帰

◇制限など

現実には、どこのサイトのどのCookieを保存しておきたいか、 なんてのは DSiブラウザ単体では知りようがないので、 あらかじめPC用ブラウザでアタリをつけてからでないと 実運用は難しいと思います。

Cookie復帰で path=expires= を含めて 復帰できない (保存時に読み出せていない) のは、 JavaScriptのセキュリティ上の仕様です。

session Cookieしか持てない (本体リセットで消えてしまう) DSブラウザー で 使えるかどうかは試していません。


HTMLソースを見る (View Source)

以下のようなURL (Bookmarklet)を「お気に入り」の「編集」で入れておけば、 現在のページのHTMLソースコードを表示できます。

javascript:function(d){ d.write('<PRE style="white-space:pre-wrap;font-size:xx-small">'+ (d.documentElement.outerHTML||d.documentElement.innerHTML). replace(/[&<>]/g,function(c){return '&#'+c.charCodeAt(0)+';'})+ '</PRE>'); }(document);

追加用補助リンク

DSiブラウザーは view-source:URLserver:source に対応してないので、 ソースを見るにはこの方法しかなさげです。 このブックマークレットを使えば opera:aboutの ソースコードも見れます。 (なぜか file:///scripts/ などのソースは見れない)


ページ情報 (page info)

[ページ情報 サンプル画面] 他のブラウザでいう「ページ情報」を表示します。 文字エンコーディング、 最終更新時間、 使っている<SCRIPT> など。
Size: はDOM経由で文字数を数えているので、概算です。 HTTPで流れてきたバイト数とは一致しません。


javascript:
## Show current page info:
## title, charset, Last-Modified, size(estimate), LINK, SCRIPT
function(d){
	var m=d.lastModified,
	l,i,lx="",jx="";

	"?";// prevent \n to be /n

	// collect <LINK>
	l=d.getElementsByTagName("link");
	for(i=0;i<l.length;i++){
		lx+=l[i].outerHTML+"\n";
	}
	// collect <SCRIPT>
	l=d.getElementsByTagName("script");
	for(i=0;i<l.length;i++){
		if(l[i].src)jx+="src="+l[i].src+"\n";
	}
	alert("
		Title: "+d.title+"\n
		Charset: "+d.charset+"\n
		Last-Modified: "+((m=="January 1, 1970 GMT")?"none":m)+"\n
		Size: "+d.documentElement.outerHTML.length+"\n
		URL: "+d.location+
		(lx?"\nLink: "+lx:"")+
		(jx?"\nScript: "+jx:"")
	)
}(document)
追加用補助リンク
いますぐ使ってみる

Object の中身を表示する (evaluate expression, and dump the Object)

Object名 (window.navigatorなど) を入力し、 中身をダンプします。 DOM探検や、javascriptプログラムの試験に使います。 一応、オフライン(インターネットにつないでいない状態)でも使えます。

[Objectの中身表示 サンプル画面]

javascript:
## Prompt an expression, and dump (enumerate) contents of that Object.
## Default expression will be the text of current selection.
## Expression evaluating to plain value will just be shown in alert().
##
## Interesting Objects:
## window
## window.navigator
## window.getSelection()
## document
## document.activeElement
## document.getElementsByTagName('SCRIPT')[0]
##
## What we want to do is
## 1. get current text selection, otherwise set default expression
## 2. prompt() for expression
## 3. eval() in *global context* (not in function)
## 4. enumerate and dump the object
## but the code is not in this order.

(
    ## This temp var will taint the global scope (window.*), so be obscure
    ## The global window._o3E will have {DontDelete} attribute, 
    ## so it is not "delete _o3E"-able.
    _o3E=function(s){
	// selection sometimes include \v for newline. strip it
	//s.replace(RegExp(String.fromCharCode(11),'g'),' ');

	## 2. prompt() for expression

	## prompt() silently truncates return value length to 49.
	## Warn if likely; otherwise, prompt()
	## (Note: editing the string in prompt() will change
	##  document.activeElement to <BODY>)
	if(s.length<50||
	   !confirm('Too long. Eval directly?:'+s)){
		// Selection was length<50, or user cancelled 
		// to edit the selection anyway.

		// Truncate to 49 before prompt() does it,
		// to show user the real thing
		s=prompt('Object expression:',s.slice(0,49));
		// [Cancel] will return undefined
	}
	return s;
    }(
	## 1. get current text selection, otherwise set default expression
	getSelection()+''||
	'document.activeElement'	//Edit this default as needed
    )
)&&
## if user cancelled, control NOTREACHED
function(s,o){
	## 4. enumerate and dump the object

	s=s.replace(/&/g,'&amp;');
	var t=typeof o,
	r=Math.floor(Math.random()*1000),
	h='<DIV id='+r+'>
	    <HR>
	## removeNode() seems to be nonstandard (MS?), but works
	    <INPUT type=button value=dismiss onclick="function(n){n.removeNode(n)}(this.parentNode)"> '+
	// title on right of [dismiss] button
	    s+'
		<DL style="font-size:xx-small;white-space:pre-wrap;">
	    ',
	i,v;
	if(t!='object'||o===null){	//typeof null is "object"!
		////g(s,[o],0);

		// If the result is "undefined", it's likely
		// a statement or function with side effect.
		// quit immediately.
		//if(t=='undefined'){alert(o);return;}
		// else it's a value. just alert() it and 
		// retain current window
		//alert(o); return
		// So, that's same thing to do
		alert(o);return;
	}
	// enumerate attributes in the object
	for(i in o){
		// Generate a <DT><DD> for an attribute.
		v=o[i],t=typeof v;
		h+='<DT>'+(
		    // object.0 as in Array should be written as object[0]
		    /^[0-9]/.exec(i)?
		    // "object[0]"
			s+'['+i+']'
		    :
		    // "object.attr"
			s+'.'+i
		)+(t=='function'?'()':'')+
		'<DD style="margin-left:2em">'+t+' ';
		// we can't use switch-case here, as it uses ===
		if(i=='toString'||i=='valueOf'){
			v=o[i]();t=typeof v;	// resolve toString()
		}
		if(t=='string'){
			h+='"'+v.slice(0,99).replace(/</g,'&lt;')+'"';
			if(v.length>99)h+='...'
		}
		if(t=='number'||t=='boolean'||v===null){
			h+=v;
		}
		h+='\n'; //purely HTML level cosmetics
	}//next i

	h+='</DL></DIV>\n';
	document.write(h);
	document.getElementById(r).scrollIntoView();
}(
	## 3. eval() in global context
	_o3E,eval(_o3E)
)
追加用補助リンク
※注:上のコードはそのままでは動きません。補助リンクを使ってください。

ちょっとしたコードを実行したいとき、 いちいち「アドレス入力」からhttp://を消して javascript:...を打ち直すのが めんどくさい場合にも重宝します。

領域選択していない際のデフォルト値は埋め込みなので、 各自で適当に変更してください。 このブックマークレットも960文字くらいあるので、 大規模改造は難しいです。


電卓 (calculator)

よくあるjavascript電卓です。が、 数字入力用のボタンもないと タッチペンデバイスでは使いにくくてしょうがないので 追加してあります。 オフラインでも使えます。

[電卓 サンプル画面]
javascript:
## Calculator.
##
function bt(v,s,i){
	return '<INPUT type=button value="'+v+'" onclick="javascript:'+s.replace(/&"'/g,function(c){return '&#'+c.charCodeAt(0)+';'})+'"'+(i?' id="'+i+'"':'')+'>'
}
document.write('
	<TITLE>Calculator</TITLE>
	<STYLE>INPUT{font-size:11px;padding:1px 4px;margin:1px 0;}</STYLE>
	<SCRIPT>
	function $(i){return document.getElementById(i)}
	var ud=[];	//undo buffer
	</SCRIPT>
	<INPUT type=text id=88 value="" align=right>'+
	  bt('=','$(88).value=function(x){with(Math){var n=eval(x);if(n!=x){ud.push(x);ud.push(n)}return n}}($(88).value)')+
	  bt('C','function(v){if(v.value){ud.push(v.value);v.value=\'\'}}($(88))')+
	  bt('undo','function(n){while(ud.length>0&&$(88).value==n){$(88).value=ud.pop()}}($(88).value)')+
	'<br>\n'+
	function(){
		var r='',b='0123456789_.,/*-+',i;
		for(;i=b.charAt(0),b.length;b=b.slice(1)){
			if(i=='_'){r+='<br>\n';continue;}
			r+=bt(i,'$(88).value+=\''+i+"'");
		}
		return r
	}()+
	  bt('del','function(s){$(88).value=s.substr(0,s.length-1)}($(88).value)')+
	'<br>\n'
);

※注:上のコードはそのままでは動きません。補助リンクを使ってください。
追加用補助リンク
いますぐ使ってみる

数式はテキストボックス選択で直接編集も可能です。 Mathオブジェクト内で計算するので、 直接数式を打てば sin(PI*2) といった計算も可能です。

こういうガジェットは使うより自分で改造するほうが面白いもんです。 この電卓での凝った機能は[undo]ボタンだけにとどめてあります。


マンデルブロー集合を描く (draw mandelbrot set)

[マンデルブロー集合]

<CANVAS> を使ってマンデルブロー集合を描きます。 ブックマークレットなので、オフラインでも存分に楽しめます。


javascript:
## Draw Mandelbrot set using <canvas>
// w=h=100;p=1; MMX 166MHz: 9sec, DSi XL: 40sec
w=h=102;p=2;	//canvas width,height, pixel size
		//h/p should be odd, to have axis rendered at center
document.write('
	<canvas id=8 width='+w+' height='+h+' style="background:#ddd">canvas</canvas>
	<br>
	<button onclick="a/=2.5;m.st()">zoom in</button>
	<button onclick="a*=2.5;m.st()">zoom out</button>
	<script>
	## return "#rgb" from iteration count i
	c=[];	//i->"#rgb" cache
	function g(i){
		return c[i]||(c[i]="#"+q(i/=7)+q(i+2)+q(i+4));
		// above works. eval order of c[i]=..i/=7.. is 
		// implementation dependent in C, but 
		// left-to-right in ECMAScript
	}
	function q(t){
		return "00123456789abcdeff".charAt(Math.cos(t)*8+9);
	}

	//aperture. smaller value will zoom
	a=2.7;	//in global to make it available from "onclick"

	m=function(d){
	//wrap in function to create activation object
	var i,
	    pr=-.8,pi=0,	//center coordinate
	    //a=3.0,		//aperture
	    cv=d.getElementById(8),
	    c=cv.getContext("2d"),
	    w='+w+',h='+h+',	//canvas size
	    p='+p+',		//pixel size
	    cr,ci,	// Complex c=(cr,ci) of rendering point
	    r,s,	// Complex Z=(r,s)
	    t,u,	// temp r*r, s*s
	    x,y,	// drawing canvas coordinate
	    f,		// #rgb temp
	    o={};	// object to return

	o.st=function(){
		c.clearRect(x=0,y=0,w,h);
		setTimeout(o.xt);
	};
	o.xt=function(){
		## async implementation of outer loop "for(y=0; y<h; y+=p)"
		if(!(y<h)){	//end loop
			d.close();
			return
		}

		## -p/2 to calculate at the center of pixel
		ci=(h/2-p/2-y)*a/h+pi;
		## inner loop of x not async-ized here
		for(x=0;x<w;x+=p){
			cr=(x-w/2+p/2)*a/w+pr;
			## iterate
			for(i=99,r=s=0;i>0&&4>(t=r*r)+(u=s*s);i--){
				## iterate z = z^2 + c
				## until |z|>2, which is proved to diverge to infinite
				t=t-u+cr;s=2*r*s+ci;r=t;		//standard Mandelbrot set
				//t=t-u+cr;s=-Math.abs(2*r*s+ci);r=t;	//flaming bird
			}
			## draw pixel
			c.fillStyle=(f=i?g(i):"#000");
			//d.write("\\nx"+cr+","+ci+"i"+i+f);
			c.fillRect(x,y,p,p);
		}

		y+=p;
		//"this.xt" is invalid since "this" isn't available, 
		// so use "arguments.callee" to self-refer
		//setTimeout(arguments.callee,0);	//yield&repeat
		setTimeout(o.xt);	//yield&repeat
	}; //.xt

	//set addEventListener to pick up Event struct;
	//"onclick=" will not have Event arg
	cv.addEventListener("click",function(e){
		pr+=(e.clientX-cv.offsetLeft-w/2)*a/w;
		pi+=(h/2-e.clientY+cv.offsetTop)*a/h;
		o.st();
	},!1);

	return o;

	}(document); //m
	m.st();
	</script>'
);
追加用補助リンク
いますぐ使ってみる

CPUの数値演算を酷使するベンチマークにもなります。 MMX 166MHz の Opera と比べると 1/4くらいのスピードなので、 JavaScriptのスピードだけ見ればDSiブラウザはPentium 50MHz程度の性能? 3GHz CPUでは1秒で終わる無駄に重いスクリプトは、 DSiでは1分かかることになる勘定です。 体感でもそんなものですね。

正直、1000文字以内のブックマークレットでここまでやる必要あんのか? という気はしますが。


文句

◇補助リンクのURLの癖

Operaの癖で、<A href>中のURLの、 "?" 以前にある "\" は 勝手に "/" に置換されてしまう。 ブックマークレットを補助リンク経由で配布する際は、 コードに"\" が含まれないよう細工するか、 コードの冒頭のどこかで ? を使います。 三項演算子(a?b:c)や、'?';を 事前に入れておけば十分。 手打ちするぶんには問題ない。

ブックマークレット補助リンクの href 中が、 「# の後にマルチバイト文字がある」となっていると、 お気に入りに「ページを追加」した時に # 以前の空白や < > がすべて return%20c; などと escape() されてしまう。 javascriptとしては当然動かない。 スクリプトによっては引っかかる場合がある。

なので普通の補助リンクは <A href="#javascript:...."> などとしておくわけですが、 当ページでこの方法をとってないのは、クリック後のエラー画面で せめてスクリプトの断片を確認したいから。 エラー画面では # 以降は表示されないので、"#javascript:" とすると 主URLしか表示されず、本当に補助リンクを踏んだのか不安になる。

|が打てない

スクリーンキーボードでは "|" (VERTICAL BAR, OR演算子) が 入力できません。 Shift+@ の位置に出ているのは全角の "|" です。 手書きでも出てきません。 半角の "|" をDSi単独で入力する方法は無いかも… バグだと思う。

補助リンクで配る分には問題なく投入できます。 お気に入りの「編集」の手打ちでどうしても使いたい場合は、 三項演算子で代用するか、 ド・モルガンの法則で論理を反転してやります。
例: if( a || b ) → if (a?1:b) if(!(!a && !b))

なお DSi上では | は broken bar に見えます。

ブックマーク長の1000文字制限

DSiブラウザーのブックマークは1000文字くらいの制限があるので、 あまり巨大なブックマークレットは登録できません。 DSブラウザーはもっと大きなブックマークを作れたみたいですが…

正確にいうと、長いURL自体は お気に入り に追加できるが、 アドレスを「編集」するエディタが1000文字以上を扱えない。 補助リンク経由での配布ではどうしても「編集」が必要なので、 この制限はけっこう痛い。

prompt()の49文字制限

DSiブラウザーのprompt()の返り値は49文字に制限されています。 表示や入力文字列の編集はもっと余裕がありますが、 返り値は49文字で強制的に切られてしまうので、

javascript:alert(eval(prompt('Expression:','')))
で長いコードを入れると途中で切られてエラーになる(何も起こらない) ことがあります。

49文字って意外と短いですよ?プログラムコードだと2行くらい。

unescape(escape(...))が元に戻らない

escape("\r\nほげ") は %0D%0A%u307B%u3052に変換されますが、 unescape()が%0D%0Aを戻せず止まる。 どうやったらそんなバグ作れるんだ。 .replace(/%0/g,'%u000')とかでむりやりUnicode化(%u000A%u000D)すると 戻せるようになる。

document.activeElementが親を拾う

例えば <A href="content.html" target="body"> で別ページを表示すると、 DSiブラウザー内では FRAME か 別タブを開くようになっているのか、 新ページでフォーカスを移動させても document.activeElement では新ページではなく 親ページの <A href> のほうが拾われてしまいます。 別タブになっているかどうかは DSiブラウザー上では全くわかりません。

document.activeElement は元々は Internet Explorer 4の独自拡張ですが、 Mozilla系列にも似たようなバグがあった、らしい、ので、 修正がめんどくさい部類のバグのようです。

DSi側での対処法: 一旦、右下ボタン→「www アドレス入力」→[移動] (編集は不要)で、 FRAME状態を解除する。

最上位DOMノード #document のparentNodeを拾うと止まる

onclickに何があるか探索するなど、 DOMノードを順次親に向かって探索しようと素直に

	a=document.activeElement;
	for(p=a; p; p=p.parentNode){
		...
	}
とすると、最上位ノード (p.nodeName=="#document")で p.parentNode を 拾う時に問答無用で停止します。ので
	for(p=a; p && p.nodeName!='#document'; p=p.parentNode){
		...
	}
みたいな不細工な停止条件が必要です。


サンプル画面がトイカメラっぽいんですけど

スクリーンショットを撮っているデジカメが 本体内部の赤外線を拾ってるっぽいです。 なので中央が白っぽく写るようです。 まぁデジカメもそんな高級品ではないですが。


リンク

任天堂:社長が訊く『ニンテンドーDSi』 DSiブラウザー 篇
開発者インタビュー。必見

インプレス:開発陣に聞くニンテンドーDSブラウザーのコンセプト
DSブラウザーに関する、 インプレスの Broadband Watch のインタビュー。 Cookieが保存されないのは仕様にした。

ニンテンドーDSブラウザーWiki(消滅)のブックマークレットのWaybackアーカイブ
http://hammerspace.squares.net/DS/ は現在消滅。 Wayback Machineにはトップページは残ってますが、 「ブックマークレット」ページは残ってないので、 残骸らしきものにリンクを張っておきます。


かべ@sra-tohoku.co.jp