【便利!】任意のページ上にテレビ番組表を表示するブックマークレット について


任意のページ上にテレビ番組表を表示するブックマークレット
http://blog.fkoji.com/2007/03082337.html


便利です。


こういうものを常々探していましたし、自分で作ろうかとも思っていました。
毎日出かける前とか天気予報をチラッと見たい時に
「これはブックマークレット的なもの(ワンプッシュで最新の情報を得れるってあたり)がいいな」
という動機から上記サイトにたどりつきました。(今回、天気予報とは違うけど主旨は同じ)


で、このブックマークレットの利点はというと

  • タブを消費しない(私は毎日番組表のタブを1つキープしていました;)
  • 気になったときにだけ実行すればよい(そして最新の情報が得れる)
  • すぐに読み込んだ情報を消せる
  • URLさえ変更すれば色々なサイトに応用できる(汎用性が高い!)

タブブラウジングな時代ですが、消費リソースは最小限にしたいものですね。


で、今回オリジナルの記事主さんに了承を得たのでこっちでコードを追ってみようと思います。
(オリジナルのコードは記事主F.Ko-Ji さん、以下のコードは上記エントリー内のコメント欄でポストしてくれているoshiro さんのを元に更に改良しました)
※コードはヒューマンリーダブルにしてありますのでブックマークレット化するにはソース下にあるコードからうまいことから行ってくださいませw


ソース

javascript:
/*イベント定義1*/
var%20tvmap_func=function(){
	with(document.getElementById('tvmap').style){
		top=(document.body.scrollTop||document.documentElement.scrollTop)+"5px";
	}
};
/*イベント定義2:body部をクリックすれば消せるようにした*/
var%20remove_func=function(evt){
	if(document.getElementById('tvmap')&&evt.target.id!="tvmap"){
		document.body.removeChild(document.getElementById('tvmap'));
	}
};
/*ブラウザごとにイベント登録方法を分ける*/
if(window.attachEvent){
	window.attachEvent('onscroll',tvmap_func);
	window.attachEvent('onclick',remove_func);
}else{
	window.addEventListener('scroll',tvmap_func,false);
	window.addEventListener('click',remove_func,false);
}

(function(){
	/*urlを変えればいろんなサイトに応用できる*/
	var%20url="http://tv.yahoo.co.jp/vhf/tokyo/realtime.html";
	var%20d=document.createElement("div");
	d.id="tvmap";
	with(d.style){
		textAlign="right";
		backgroundColor="#eee";
		width="95%";
		height="55%";
		filter='alpha(opacity=95)';
		opacity=.95;
		position="absolute";
		top=(document.body.scrollTop||document.documentElement.scrollTop)+"5px";
		left="5px";
		margin="auto";
		zIndex="100";
	}
	d.innerHTML+="";
	var%20i=document.createElement("iframe");
	i.src=url;
	with(i.style){
		width="100%";
		height="95%";
	}
	d.appendChild(i);
	document.body.appendChild(d);
})()

※動作確認FF3、IE7


はてなjavascript:プロトコルが設定できないようなので↓の赤字の手順でブックマークレット化してみてください。メンドイですね、すみません><

任意のページ上にテレビ番組表を表示するブックマークレットg
[1]↑のリンクをとりあえずブックマークに追加する
[2]↓のコードを 追加したブックマークのURL として設定すればブックマークレットになります

javascript:var%20tvmap_func=function(){with(document.getElementById('tvmap').style){top=(document.body.scrollTop||document.documentElement.scrollTop)+"5px";}};var%20remove_func=function(evt){if(document.getElementById('tvmap')&&evt.target.id!="tvmap"){document.body.removeChild(document.getElementById('tvmap'));}};if(window.attachEvent){window.attachEvent('onscroll',tvmap_func);window.attachEvent('onclick',remove_func);}else{window.addEventListener('scroll',tvmap_func,false);window.addEventListener('click',remove_func,false);}(function(){var%20url="http://tv.yahoo.co.jp/vhf/tokyo/realtime.html";var%20d=document.createElement("div");d.id="tvmap";with(d.style){textAlign="right";backgroundColor="#eee";width="95%";height="55%";filter='alpha(opacity=95)';opacity=.95;position="absolute";top=(document.body.scrollTop||document.documentElement.scrollTop)+"5px";left="5px";margin="auto";zIndex="100";}d.innerHTML+="";var%20i=document.createElement("iframe");i.src=url;with(i.style){width="100%";height="95%";}d.appendChild(i);document.body.appendChild(d);})()


おまけ
任意のページ上に天気予報を表示するブックマークレットg
[1]↑のリンクをとりあえずブックマークに追加する
[2]↓のコードを 追加したブックマークのURL として設定すればブックマークレットになります

javascript:var%20tvmap_func=function(){with(document.getElementById('tvmap').style){top=(document.body.scrollTop||document.documentElement.scrollTop)+5}};var%20remove_func=function(evt){if(document.getElementById('tvmap')&&evt.target.id!="tvmap"){document.body.removeChild(document.getElementById('tvmap'));}};if(window.attachEvent){window.attachEvent('onscroll',tvmap_func);window.attachEvent('onclick',remove_func);}else{window.addEventListener('scroll',tvmap_func,false);window.addEventListener('click',remove_func,false);};(function(){var%20url="http://weather.yahoo.co.jp/weather/jp/40/8210.html#contents-start";var%20d=document.createElement("div");d.id="tvmap";with(d.style){textAlign="right";backgroundColor="#eee";width="95%";height="55%";filter='alpha(opacity=95)';opacity=.95;position="absolute";top=(document.body.scrollTop||document.documentElement.scrollTop)+5+"px";left=5+"px";margin="auto";zIndex="100";}d.innerHTML+="";var%20i=document.createElement("iframe");i.src=url;with(i.style){width="100%";height="95%";}d.appendChild(i);document.body.appendChild(d);})()

(元々はこれがやりたかった)


Bloggerにもリンクをおいておきましたのでどうぞ!
Blogger:任意のページ上に天気予報を表示するブックマークレットg

*1

*1:zIndex="100";をプロパティとして追加しました。Nullyさんありがとうございます><;

xl2web(簡易版)を公開します!!1

↑言ってみたかっただけ ってのがほとんどw

xl2web(簡易版)
http://tools.gatchang.net/index.html#xl2web
http://bgatchan.blogspot.com/2008/12/alert1-var-ddocument-function_316.html
(リンク先を修正しました><;)


ExcelのテーブルをHTMLへ変換し、そのソースがすぐ見れるツールです。
あ、JSのコードとかは精査されたようなものではありません。過程がわかりやすければよいのでw

"タイミング"はここから。


エクセルデータからHTML Tableタグの表を作成してくれる「Tableizer」 - WEBマーケティング ブログ
http://web-marketing.zako.org/web-tools/tableizer-html-tables-from-excel.html
自分で作ろうとしてたけど、時間を取らなくて避けていた(逃げていたw)。

今までどうやってこのテキスト・ルーチンワークをやってたかと言うと、


Excel-HTML Conversion
http://www.02.246.ne.jp/~yingming/macclinic/tips/excel_html.html
こーゆーイメージのようなことを「数式で文字列くっつけくっつけ」やってた

Excelで作ってエディタで整形して仕上げ。
これもHTMLソースとしてこだわったインデントで作成しようとすると思わぬ時間を食ったり。

こんなもんHTMLで一度作っちゃえば、「ワーク」しなくていいジャンと思いつつやってなかったので今回書いてみた。
ほんとに数行で終わったのでつまんないですが。

ソース

<!--2008/12/14 22:18-->
<html>
<head>
<style type="text/css">
body {
	font-size:16px;
}

textarea {
	display:block;
	width:900px;
	height:200px;
}
.readonly {
	background-color:silver;
}

/*previewのテーブル表示設定*/
td {
	border:silver 1px solid;
	width:50px;
	padding:2px;
	font-size:14px;
}
</style>
</head>
<script type="text/javascript">
//汎用ショートカット
var d=document;
function getElm(id){
	return d.getElementById(id);
}
//IEはonchange、FFはonkeyupで反応する(FFはキーですぐ反応する仕様になる)
function doit(){
	var strTmp=getElm("before").value;
	var strTab="";
	//(frm.indent.checked == true)?strTab="\t":strTab="";//これはダメな例xxx
	(frm["indent"].checked == true)?strTab="\t":strTab="";
	strTmp=strTmp.replace(/\t/g, "</td> <td>");
	strTmp=strTmp.replace(/^/, strTab+"<tr> <td>");//【1】strTmpの先頭
	strTmp=strTmp.replace(/\n.+[^$]/g, "</td> </tr>\n"+strTab+"<tr> <td>");//【2】行ごとに<tr><td>
	strTmp=strTmp.replace(/\n*.*$/, "</td> </tr>");//【3】最後の行を</tr>で締める
	strTmp="<table>\n"+strTmp+"\n</table>";//【4】親タグtableを最後に追加
	
	getElm("after").value=strTmp;
	getElm("preview").innerHTML=strTmp;
}
</script>
<body>
<h1>ExcelのテーブルをHTMLに変換する</h1>
<form name="frm">
<h3>Excelデータ貼り付け</h3>
<textarea id="before" name="before" title="ここにExcelデータを貼り付けてください" cols="100" rows="10" onchange="doit();" onkeyup="doit();"></textarea>
<input type="checkbox" name="indent" onclick="doit();"> 適度にインデントする
<h3>HTMLコード(コピって使ってください)</h3>
<textarea id="after" class="readonly" name="after" cols="100" rows="10" readonly></textarea>
</form>
<h3>プレビュー</h3>
<div id="preview">

</div>

</body>
</html>

MS信者はHTAでもよろしいかと。


これはザックリ書いたので、
もう少し凝れば

  • 項目名に色づけ(class指定)
  • 一行ごとに色づけ(class指定):配列利用
  • もうちょいインデント
  • などなどw

けどまあ一回こんな小物作ってれば、作業コストとイライラは削減されるから。
次は、1行ごと配列に分けてもっと汎用的に。

nullとundefinedについて

更新が滞っていたため以前のメモを備忘のため転記。
(というか、Ext JSから現在離れているため:イイワケw)


今日キーワードの定義を勘違いしてしまっていた結果失敗したこと。2008/07/07 19:40:37のw


あるscope内で何度も内容が変化するオブジェクトを格納するための変数を初期化する場合
どうすればいいのだろうか。

nullはオブジェクトがないことだと思っていた。
ただ単にそういう解釈でコードを書いてしまうと失敗する。

ある定義されたオブジェクトAがあり、
そのオブジェクトのメソッドを次のように定義しているとすると

  • oDataはグローバルな変数だとする
  • oDataは最初にvar oData;と宣言されているだけだとする
function func(){
	//↓をループして評価させるロジックを書いておく
	if(typeof oData=="object"){
		//oDataを利用した処理
	}
}

そのオブジェクトAのscope内でoDataが(ユーザー操作などで)随時変化する時、
上記のtypeofでの評価は2回目以降はoDataが更新されずにtrueと評価されてしまう可能性が高くなる。


そこで、nullと設定すると、

function func(){
	oData=null;//初期化=2回目以降のtypeof評価のため必須
	//↓をループして評価させるロジックを書いておく
	if(typeof oData=="object"){//ここでエラー
		//oDataを利用した処理
	}
}

今度は、undefinedと設定すると、

function func(){
	oData=undefined;//初期化=2回目以降のtypeof評価のため必須
	//↓をループして評価させるロジックを書いておく
	if(typeof oData=="object"){
		//oDataを利用した処理
	}
}

随時変化するoDataに応じた処理ができた。
ここで大切なのはoDataを正しく(?)初期化できたこと、である。

定義時の初期化(しかしながら内容は未定義である状態)
var oData;
を再現する方法は
oData=undefined;
これでグローバルでありながら関数内でも初期化できる?

JavaScript的にこれでいいのではないだろうか・・・これでうん、たぶん・・・

Ext.extendのパターン

Ext.extendの手続き方法とかその辺まだよくわかってないので整理のため記。


//独自のグリッドパネルを生成する
oGrid = function(){
	GridP1.superclass.constructor.call(this, {
		title	: "グリッドタイトル",
		//		・・・あと他のオプションを諸々設定する・・・

	});
	this.on('rowcontextmenu', this.onRowContextMenu, this);	//【1】イベントが↓で定義されている(正確に言うとメソッド)
}

//Ext.grid.GridPanelを継承する
Ext.extend(oGrid, Ext.grid.GridPanel, {
	onRowContextMenu : function(grid, rowIndex, e){			//【2】★ここで定義されている
	//		・・・以下省略・・・

【2】の行が定義部分とすると
【1】の行は実装する部分となる。

【1】の時点でoGridオブジェクトのイベントとして登録されている。
つまり、
【1】の時点でoGridオブジェクトのメンバ?(この表現でいいかな・・・)となってることになる?
う〜ん、
メンバに入ってると言ってもonメソッドに引数を与えているタイミングでと言った方が正確か。
そう考えると自然な気がしてきた・・・。

ちなみに、
GridPanelクラスを継承するだけなら
Ext.extend(oGrid, Ext.grid.GridPanel);
だけでよいと思う。
この場合、oGridをnewしたGridPanelで生成することと同じ作業だと現段階では思っている。

なので、新たにconfigを設定しないのであれば、ただ単に
SuperClassでPanelをつくる
と言うことなのである。


今回引っかかったのは、
イベントの登録時 に その内容を後述したもの を持ってきてもよい
ということ。

要は、定義→登録 という流れじゃなくてOKという結論。

Ext JSにてthis[1]

thisとはJavaScriptのキーワードである。
Extというよりは根本的なJSの方の勉強不足かな。


あるPanelオブジェクトのButtonsプロパティ(オプション)が

buttons:[{
	id	:"btn1",
	text	: 'ボタン1',
	handler: function(){
		alert(this.id);
	}
},{
	id	:"btn2",
	text	:'ボタン2',
	handler: function(){
		//テキトーな処理
	}
}]

という設定だとして、
さらにhandler:の(functionの)中でthisを使用すると
thisっていうのは一体どのオブジェクトを参照するのか?
(またはどういう表現になるのか?)
alert(this.id);ってやって試してみた。
すると・・・

【答】btn1 と表示された。
(まあ当たり前っちゃ当たり前なんだけども・・・)

この場合のthisは以下のオブジェクトだということができる。

{
	id	:"btn1",
	text	: 'ボタン1',
	handler: function(){
		alert(this.id);
	}
}

※this.toSource();でやるとExt独自のキーワードがチラッと入っているので、同一とは言えないがコード上のイメージとしては上記のオブジェクトとして考えてもいいと思う。

ということは、
thisは
直近のオブジェクトを形成させる[{で形成された(自分自身を含む)オブジェクト
ということが言える。

これはExt全般でオブジェクトを生成する際にも言えると思う。

今日違う場面でthisで手間取ったので整理のため備忘記。
以上、thisについての自分なりのメモ。

ユーザーによりメニューアイテムを切り替える(desktop)

Ext JSのデスクトップサンプルをカスタマイズ。

  • 直前にログイン画面(別途HTML)にてログインしている
  • クッキーにてどのユーザーがログインしたのか判断できている
  • ユーザーによって表示できるメニューが変化する
  • 一般ユーザーにはメンテナンスメニューは表示されない

以上の状況を再現するためのサンプル。

ユーザーによって切り替えるデスクトップメニュー

ザックリ前置き

  • createWindowS : 別途ウィンドウ描画関数、省略
  • createWindowM : 別途ウィンドウ描画関数、省略
  • getCookieVal : 別途クッキー取得用関数、省略(サンプルにはない)
/***ユーザーによってメニュー数を変える準備***/
function setItem(ths)//変数乱用したくないため関数化
    var item1=[{ //メンテナンスユーザー
            text    : "サブ_検索",
            handler : MyDesktop.MyMenuModule.createWindowS,
            scope   : MyDesktop.MyMenuModule,
            windowId: 'MyM'+(++windowIndex)
        },{
            text    : "サブ_メンテナンス",
            handler : MyDesktop.MyMenuModule.createWindowM,
            scope   : MyDesktop.MyMenuModule,
            windowId: 'MyM'+(++windowIndex)
        }];
    var item2=[{ //一般ユーザー
            text    : "サブ_検索",
            handler : ths.createWindowS,
            scope   : ths,
            windowId: 'MyM'+(++windowIndex)
        }];
    //クッキーの値によってitemを変える
    var setItem=(getCookieVal("loginid")=="hogehoge")?item1:item2;
    return setItem;
}
/***ユーザーによってメニュー数を変える準備end***/

MyDesktop.MyMenuModule = Ext.extend(MyDesktop.MyModule, {
    init : function(){
        this.launcher = {
            text    : 'AAA サブメニュー',
            menu    : {
                //itemsをユーザーによって切り替える
                items:setItem(this)
            }
        }
    }
});
//【細かいコードは省略】


もともとのコードはこんなカンジ
と言っても、サンプルにサブメニューはないが。

MyDesktop.MyMenuModule = Ext.extend(MyDesktop.MyModule, {
    init : function(){
        this.launcher = {
            text    : 'AAA サブメニュー',
            menu    : {
                items:[{
                    text    : "サブ_検索",
                    handler : this.createWindowS,
                    scope   : this,
                    windowId: 'MyM'+(++windowIndex)
                },{
                    text    : "サブ_メンテナンス",
                    handler : this.createWindowM,
                    scope   : this,
                    windowId: 'MyM'+(++windowIndex)
                }]
            }
        }
    }
});

item生成を関数で包むメリット

  • 変数が乱用されない
  • メインコードからのthisを受け取れる

setTimeoutについてのまとめ[2]

まとめというかサンプル。

【EX3】Ajaxなどの非同期な処理結果を受けての次処理の実行
=Ajaxのレスポンスデータが次の処理に影響する場合など

  • Ajax1() : Ajaxでの通信処理結果が返ってきたらres1としてオブジェクトになる
  • Ajax2() : Ajax1と同じ内容の処理

※全ての動作は関数(静的なもの)として処理を走らせないようにすることが前提である(不安定なレスポンス処理が終わればどうでもよいが)

function next(){
    Ajax2(0, 0);                //【4】
    (function aaa(){            //【5】
        if(typeof res2=="object"){
            jsFunction1();        //【6】wait2終了後
            jsFunction2();        //【7】wait2終了後
            return;
        }
        setTimeout(arguments.callee, 100);//wait2
    })();
}

Ajax1();                    //【1】
(function aaa(){                //【2】
    if(typeof res1=="object"){
        next();                //【3】wait1終了後
        return;                //【8】プロセス完了
    }
    setTimeout(arguments.callee, 100);    //wait1
})();

処理の流れを書いてみた。流れがわかる程度に。
JS側で同期を取る必要がある場合こういったwaitが必要になってくる。


【流れ】
Ajax1を処理させてAjax2を処理したいが、Ajax1でのレスポンスres1がちゃんとオブジェクトになっていないとエラーになる。
なので、確実にレスポンスを待って、そのタイミングでAjax2を処理する。
Ajax2でのレスポンスが確実に帰ってきたら、そのタイミングでJS側での関数jsFunction1, jsFunction2 を順次処理する。


この部分(別に名前aaaは必要ない)は関数化したかったが、

(function aaa(){
    if(typeof res1=="object"){
        next();    
        return;
    }
    setTimeout(arguments.callee, 100);    //wait
})();

関数化したとして、

  • res1を文字列で渡す、処理内でeval()し、評価を繰り返す。
  • だけども関数化して関数に投げると結局またそれをそのタイミングで受け取らないといけないワケで・・・。受け取り側でもwait?
  • なんか本末転倒な感じがする・・・

とかってやってたらうまく同期が取れなかった・・・りして、面倒だが凡長な繰り返し記述するだけにした。
今後の課題とする。いい方法ないかしら・・・。