ぼくはまちちゃん!(Hatena)

  • ライブドアリーダーで読む → Subscribe with livedoor Reader
ぼくはまちちゃん!のRSSフィード

2009/01/19

tPod - tumblrのダッシュボードに流れる画像を眺めるガジェット

はい! こんにちはこんにちは!!
そんなわけでAIRアプリつくってみました!

tPod
tPod - tumblrのDashboardに流れる画像を眺めるガジェット

こんなやつです!
tumblrの自分のdashboardの画像が次々に流れてきます!
ちょっとかわいいよ!

tumblrなんて知らないよ…!

なんて人もこの機会に、ちょっと試してみたらどうでしょうか!
なんか気に入った画像や言葉を、みんなでどんどんクリップ(引用)していくだけのサービスで楽しいよ!

http://www.tumblr.com/
ここから、メールアドレス入力すれば、すぐ登録できるよ!

登録したら適当に、ぼくとか以下のような人たちを Follow しておけば、ダッシュボードに画像が流れてくるんじゃないかな!

http://hmcy.tumblr.com/
http://yuiseki.tumblr.com/
http://sui.tumblr.com/
http://acqua.tumblr.com/
http://ot-inc.tumblr.com/
http://onaho.tumblr.com/
http://gokujo.tumblr.com/


tPodのインストール

Install Now を押してね!


tPodのソースコード一式 (v0.84)

http://hamachiya.com/air/tpod/tpod_src084.zip


関連記事


バージョンアップ履歴

  • v0.84 ... いつのまにかパースエラーで動かなくなってた(画像が表示されなくなってた)から修正
  • v0.83 ... バージョンチェックの後、このページを開くときは標準ブラウザで開くように、tPodを終了させるようにした
  • v0.82 ... 窓の位置が外側にハミ出た状態で終了すると、次回からちゃんと起動しなくなってた
  • v0.81 ... バージョンチェックのところに余計なコードがあったからけした
  • v0.8 ... 独自ドメインを設定してる人がdashboardに現れると止まってた。あと起動時にバージョンチェックするようにしたよ
  • v0.7 ... 同じページをぐるぐる表示してた。あとreblogedの部分が表示されていない時があった
  • v0.6 ... 起動してすぐに(tPodのロゴが出ている時に)windowのサイズを変えようとするとボタン類が見えなくなったりするバグを修正
  • v0.5 ... 最初の公開バージョン

tPodのJavaScript部分のソースコード (800行くらい)

// v0.84
air.trace('start tPod.');

var tPod = {
	cfg: {
		versionCheckPage: 'http://hamachiya.com/air/tpod/version',
		installPage: 'http://d.hatena.ne.jp/Hamachiya2/20090119/tPod',
		tumblrRoot: 'http://www.tumblr.com',
		tumblrSave: 'http://www.tumblr.com/share?v=3',
		tumblrDashboard: 'http://www.tumblr.com/dashboard',
		checkLoginInterval: 180000,
		checkLoginInterval_nologin: 12000,
		changeInterval: 9200,
		requestInterval: 240000,
		page: 4,
		paddingHeight: 129 // メイン表示部以外に必要な縦幅
	},
	initData: [],
	initDataList: [],
	rawPosts: [],
	posts: [],
	formKey: null,
	login: null,
	elm: {},
	settingWindow: null,

	checkLoginTimer: null,
	requestTimer: null,
	changeTimer: null,
	redrawTimer: null,
	retryTimer: null,

	version: new DOMParser().parseFromString(air.NativeApplication.nativeApplication.applicationDescriptor, "text/xml").getElementsByTagName('application')[0].getElementsByTagName("version")[0].firstChild.data
};

air.trace(tPod.version);

// tpod.iniファイルを読み込むよ
readInitData();

// windowの位置と幅を復元するよ(なければデフォルトサイズ)
positioning(200, 306); // default size

document.observe('dom:loaded', initialize, false);

function initialize() {
	tPod.elm = {
		loginStatus: $('loginStatus'),
		viewOuter: $('view'),
		view: $('main'),
		postInfo: $('postInfo'),
		message: $('message'),
		settingWindow: $('settingWindow'),
		btMain: $('btMain'),
		btBack: $('btBack'),
		btNext: $('btNext')
	};

	// tpod.iniファイルから読み込んだ設定データをtPod.cfgにいれるよ
	setConfig();

	// メイン表示部を正方形にするよ
	tPod.elm.viewOuter.style.height = tPod.elm.viewOuter.offsetWidth;

	// tPodのロゴ。じんわりだすよ
	new Effect.Appear($('tPodImage'), { 
		from: 0.1,
		to: 1.0,
		delay: 1.2, // 開始までの秒数 
		fps: 60,
		duration: 3.2,
		beforeStartInternal: function(effect) {
		}, 
		afterFinishInternal: function(effect) { 
			// 一時停止・再生ボタンのイベント
			tPod.elm.btMain.addEventListener('click', hPause, false);
		} 
	});

	$('ver').innerHTML = tPod.version;
	checkVersion();

	// ボタンのhover画像
	// cssでやると何をどうやっても初回ちらつくからこっちでやるよ
	tPod.elm.btBack.addEventListener('mouseover', function() {
		tPod.elm.btBack.src = 'img/tri_l_hi.png';
	}, false);

	tPod.elm.btBack.addEventListener('mouseout', function() {
		tPod.elm.btBack.src = 'img/tri_l.png';
	}, false);

	tPod.elm.btNext.addEventListener('mouseover', function() {
		tPod.elm.btNext.src = 'img/tri_r_hi.png';
	}, false);

	tPod.elm.btNext.addEventListener('mouseout', function() {
		tPod.elm.btNext.src = 'img/tri_r.png';
	}, false);

	// 一度ログインチェックに入るとsetTimeoutで一定間隔ごとにチェックするよ
	checkLogin();

	// dashboardの中身を定期的にtPod.cfg.page分とりにいくよ
	// とってきたデータ(html)はtPod.postsに格納するよ
	getDashboard(true);
	tPod.requestTimer = setInterval(getDashboard, tPod.cfg.requestInterval);

	// windowをリサイズされてもメイン表示部は正方形になるようにするよ
	window.addEventListener('resize', hResize, false);

	// 次々に画像を切り替えるやつだよ
	tPod.changeTimer = setInterval(changer, tPod.cfg.changeInterval);

	// 終了時にはwindowの位置とかをtpod.iniに書き出すよ
	nativeWindow.addEventListener('closing', saveInit, false);

	// 設定画面ひらくやつ
	$('openSettings').addEventListener('click', function() {
		openSettings();
		tPod.elm.settingWindow.toggle();
	}, false);
	$('cancel').addEventListener('click', function() {
		tPod.elm.settingWindow.hide();
	}, false);
	$('save').addEventListener('click', function() {
		tPod.elm.settingWindow.hide();
		saveSettings();
	}, false);

	// デバッグ用のログを表示させるやつだよ
	$('viewLog').addEventListener('click', function() {
		if ($('log').style.display == 'none') {
			nativeWindow.height = Number(nativeWindow.height + 94);
			$('log').show();
		} else {
			nativeWindow.height = Number(nativeWindow.height - 94);
			$('log').hide();
		}
	}, false);
}

// 起動時に呼ばれるよ。 tpod.iniファイルを読み込む
function readInitData() {
	air.trace('read tpod.ini');
	// C:\Users\****\AppData\Roaming\Hamachiya2.tPod\Local Store\tpod.ini
	var file = new air.File('app-storage:/tpod.ini');
	// air.trace(file.nativePath);
	var fs = new air.FileStream();
	var data = '';

	try {
		fs.open(file, air.FileMode.READ);
		var data = fs.readUTFBytes(fs.bytesAvailable);
	} catch (e) {
	}

	fs.close();

	// air.trace(data);
	var dataRows = data.split('\n');
	dataRows.map(
		function(v) {
			var temp = v.split('\t');
			if (temp[1]) {
				// tPod.initData[temp[0]] = temp[1];
				tPod.initData[temp[0]] = temp[1];
				tPod.initDataList.push(temp[0]+'\t'+temp[1]);
				air.trace(temp[0] + '\t' + temp[1]);
			}
		}
	);
}

// tpod.iniファイルから読み込んだデータがあればtPod.cfgにいれるよ
function setConfig() {
	if (tPod.initData.changeInterval) {
		tPod.cfg.changeInterval = tPod.initData.changeInterval;
	}
	if (tPod.initData.requestInterval) {
		tPod.cfg.requestInterval = tPod.initData.requestInterval;
	}
	if (tPod.initData.bufferedPages) {
		tPod.cfg.page = tPod.initData.bufferedPages;
	}

}

// 起動時に呼ばれるよ。windowの位置や幅を復元
function positioning(w, h) {
	var xx, yy, ww, hh;

	if (tPod.initData.bounds) {
		// 外側にはみでてた時、マイナスが入ることも…
		if (tPod.initData.bounds.match(/x=(-{0,1}[\d\.]+)/)) {
			xx = RegExp.$1;
		}
		if (tPod.initData.bounds.match(/y=(-{0,1}[\d\.]+)/)) {
			yy = RegExp.$1;
		}
		if (tPod.initData.bounds.match(/w=([\d\.]+)/)) {
			ww = RegExp.$1;
		}
		if (tPod.initData.bounds.match(/h=([\d\.]+)/)) {
			hh = RegExp.$1;
		}
	}

	if (xx && yy && ww && hh) {
		nativeWindow.bounds = new air.Rectangle(xx, yy, ww, hh);
	} else {
		nativeWindow.width = w;
		nativeWindow.height = h;
	}

	// 最初はtpod.xmlで非表示(visible:false) にしてあるから、ここで初めて表示
	nativeWindow.visible = true;
}

// settingsの画面のフォームのデフォルト選択状態を変更するよ
function openSettings() {
	function setSelector(elm, cfg, sec) {
		var n = cfg;
		n = sec ? n / 1000 : n;
		for (var i=0; i<elm.length; i++) {
			if (elm[i].value == n) {
				elm[i].selected = true;
				break;
			}
		}
	}

	setSelector($('change'), tPod.cfg.changeInterval, true);
	setSelector($('request'), tPod.cfg.requestInterval, true);
	setSelector($('page'), tPod.cfg.page, false);
}

// settingsの画面のsave押されたあと
function saveSettings() {
	var fChange = $F($('change'));
	if (fChange) {
		tPod.cfg.changeInterval = fChange * 1000;
		if (tPod.changeTimer) {
			clearTimeout(tPod.changeTimer);
			tPod.changeTimer = setInterval(changer, tPod.cfg.changeInterval);
		}
	}

	var fRequest = $F($('request'));
	if (fRequest) {
		tPod.cfg.requestInterval = fRequest * 1000;
		if (tPod.requestTimer) {
			clearTimeout(tPod.requestTimer);
			tPod.requestTimer = setInterval(getDashboard, tPod.cfg.requestInterval);
		}
	}

	var fPage = $F($('page'));
	if (fPage) {
		tPod.cfg.page = fPage;
	}

	saveInit();
}

// tpod.iniファイルに、windowの位置とか色々書き出すよ
function saveInit() {
	air.trace('save tpod.ini');

	//air.trace(tPod.initDatalist);
	//air.trace(tPod.initDataList.length);
	//air.trace(tPod.initDataList[0]);

	// AIRアプリと同じ位置にiniファイルつくりたいけど…
	// var file = air.File.applicationDirectory.resolvePath('tpod.ini');
	// セキュリティ回避しないと書けない
	// しかもアンインストール/再インストール時に問題が残る
	// file = new File(file.nativePath);

	// しかたないので、ややこしい位置に
	// C:\Users\****\AppData\Roaming\Hamachiya2.tPod\Local Store\tpod.ini
	var file = new air.File('app-storage:/tpod.ini');
	var fs = new air.FileStream();
	fs.open(file, air.FileMode.WRITE);
	fs.writeUTFBytes('bounds\t' + nativeWindow.bounds + '\n');
	fs.writeUTFBytes('changeInterval\t' + tPod.cfg.changeInterval + '\n');
	fs.writeUTFBytes('requestInterval\t' + tPod.cfg.requestInterval + '\n');
	fs.writeUTFBytes('bufferedPages\t' + tPod.cfg.page + '\n');
	fs.close();
}


// 一時停止・再生ボタンで呼ばれるところ
function hPause() {
	// load中(getDashboard)ならなにもしない
	if (tPod.elm.btMain.className == 'load') {
		return;
	}

	if (tPod.changeTimer) {
		// タイマーがあるってことは、一時停止したらいいの?
		clearTimeout(tPod.changeTimer);
		clearTimeout(tPod.requestTimer);
		tPod.changeTimer = null;
		message('&raquo;Pause', 1.8);
		tPod.elm.btMain.className = 'play';
	} else {
		// タイマーがないなら、再生すべき?
		message('&raquo;Play', 1.8);
		changer();
		tPod.requestTimer = setInterval(getDashboard, tPod.cfg.requestInterval);
		tPod.changeTimer = setInterval(changer, tPod.cfg.changeInterval);
		tPod.elm.btMain.className = 'pause';
	}
}

// もどるボタン
function hBack() {
	air.trace('go back');
	// なんか境界ややこしい
	// air.trace('count:'+tPod.count);
	// air.trace('length:'+tPod.posts.length);

	// nullとかだったら0にするよ
	tPod.count -= 0;

	// とりあえず2くらい引いてみる。勘で。
	var c = tPod.count - 2;

	// air.trace('a' + c);
	if (c == -1) {
		c = tPod.posts.length - 1;
		// air.trace('b' + c);
	} else if (c == -2) {
		c = tPod.posts.length - 2;
		// air.trace('c' + c);
	}

	if (c < 0) {
		c = 0;
	}

	tPod.count = c;
	// air.trace('changed:'+tPod.count);
	// air.trace(tPod.posts[c].image.src);

	// trueにするとエフェクト控えめ
	changer(true);
}

// すすむボタン
function hNext() {
	air.trace('go go next');
	changer(true);
}

// windowリサイズ時にviewが正方形になるように調整するやつ
function hResize() {
	// 画像だしたままだと画像の横幅より小さくリサイズした時に比率が崩れちゃう
	var image;
	if (image = $('mainImage')) {
		image.style.display = 'none';

		resizeWindow();
		adjustImage(image);

		// ※リサイズ終了時に普通にshowしてもタイミングが合わずうまくいかない
		tPod.redrawTimer = setTimeout(function() {
			image.style.display = ''; // show
		}, 30);
	} else {
		resizeWindow();
	}
}

function resizeWindow() {
		var vw = tPod.elm.viewOuter.offsetWidth;
		tPod.elm.viewOuter.style.height = vw;

		// windowの高さが変わってもpanelとかが隠れないように調整するよ
		var h = Number(vw + tPod.cfg.paddingHeight);
		if (h != nativeWindow.height) {
			nativeWindow.height = h;
		}
}

// だれそれさんのreblogとかでるとこ
function info(str) {
	tPod.elm.postInfo.innerHTML = str;
}

// メインの画像表示の上にかぶせて Play とか Pause とかだすよ
function message(str, t) {
	tPod.elm.message.innerHTML = str;
	tPod.elm.message.style.opacity = '0.85';
	tPod.elm.message.show();

	// tミリ秒後にじわりときえる
	if (t) {
		new Effect.Appear(tPod.elm.message, { 
			from: 0.85,
			to: 0.1,
			delay: t, // 開始までの秒数
			fps: 60,
			duration: 0.3,
			beforeStartInternal: function(effect) {
			}, 
			afterFinishInternal: function(effect) { 
				tPod.elm.message.innerHTML = '';
				tPod.elm.message.hide();
			} 
		});
	}
}

// dashboardのデータ取得してtPod.postsにどんどん格納するやつ
// なんか配列とか色々ムダが多くてごめん
function getDashboard(noMessage) {
	// ボタンのところ、ぐるぐるさせるよ
	tPod.elm.btMain.className = 'load';
	air.trace('get dashboard ...');

	if (tPod.retryTimer) {
		clearTimeout(tPod.retryTimer);
		tPod.retryTimer = null;
	}

	if (!tPod.login) {
		tPod.retryTimer = setTimeout(function() {
			getDashboard(noMessage);
		},5000);
		return;
	}

	noMessage || message('&raquo;Loading', 1);

	tPod.rawPosts = [];
	tPod.posts = [];
//	tPod.images = [];

	for (var i=0; i<tPod.cfg.page; i++) {
		(function(page) {
			air.trace('Request: ' + tPod.cfg.tumblrDashboard + '/' + page);
			var ajax = new Ajax.Request(
				tPod.cfg.tumblrDashboard + '/' + page + '?t=' + new Date(), {
					method: 'get', 
					onComplete: function(xhr) { 
						pageCollector(xhr, page, ajax.url);
					}
				}
			);
		})(i+1-0);
	}
}

function pageCollector(xhr, page, url) {

	air.trace('success: ' + url.replace(/t=.+/, ''));
/*
	var srcs = xhr.responseText.match(/src="(http:\/\/data.tumblr.com[^"]+)/g);
	tPod.images = tPod.images.concat(srcs);
	tPod.count = 0;
*/
	//var xml = xhr.responseXML;
	var text = xhr.responseText;

	// 「&」の出現によるXMLパースエラー回避
	// <img src="/images/logo.png?alpha&5" ←なんかこいつのせい?

	// scriptによるxmlパースエラー回避
	text = text.replace(/\n/g, '');
	text = text.replace(/<script.+?<\/script>/g, '');

	//air.trace(text.match(/&[^#]\w*/g));
	// これは不安だね。リンクのurlに「&」がそのままあった時とか…
	// それでもパースエラーになるかなー。まあいいか
	text = text.replace(/&[^#]\w*/g, '');

	// 取得したhtmlテキストをDOMにしちゃうよ
	var parser = new DOMParser();
	var xml = parser.parseFromString(text, 'text/xml');
	//air.trace(xml.childNodes.length);
	//air.trace(xml.childNodes[0].nodeName);
	//air.trace(xml.getElementsByTagName('html')[0].innerHTML);

//	tPod.rawPosts[page-1] = tPod.rawPosts.concat($A(xml.getElementsByClassName('post')));
	tPod.rawPosts[page-1] = $A(xml.getElementsByClassName('post')) || 1;

	// 全部のページが取れたかチェック
	var checkAll = true;
	for (var i=0; i<tPod.cfg.page; i++) {
		if (! tPod.rawPosts[i]) {
			air.trace('check page'+(i+1-0)+': NG');
			checkAll = false;
			// break;
		} else {
			air.trace('check page'+(i+1-0)+': OK');
		}
	}

	// 全ページ取れていたらなんかするよ
	if (checkAll) {
		// rawPostが [page][posts] ってなってるから
		// rawPosts[posts]になるようにするよ
		var temp = [];
		for (var i=0; i<tPod.rawPosts.length; i++) {
			temp = temp.concat(tPod.rawPosts[i]);
		}
		tPod.rawPosts = temp;

		// rawPosts を tPod.posts に入れなおすよ
		for (var i=0; i<tPod.rawPosts.length; i++) {
			if (tPod.rawPosts[i] == 1) {
				continue;
			}

			postCollector(i, tPod.rawPosts[i]);
		}

		air.trace('Posts: ' + tPod.rawPosts.length);
		air.trace('Images: ' + tPod.posts.length);

		message('&raquo;Buffer:' + tPod.posts.length, 1);
		tPod.count = 0;
	}
}

function postCollector(no, rawPost) {
	var image = rawPost.getElementsByClassName('image')[0];
	var postInfo = rawPost.getElementsByClassName('post_info')[0];
	var postControls = rawPost.getElementsByClassName('post_controls')[0];
	var avatar = rawPost.getElementsByClassName('avatar')[0];
	if (avatar.innerHTML) {
		// ***.tumblr.comの***をリンク文字にするよ
		if (avatar.href.match(/:\/\/(\w+)\.tumblr/)) {
			avatar.innerHTML = RegExp.$1;
		} else if (avatar.title) {
			// そっかtumblrって独自ドメインも使えるんだね…
			avatar.innerHTML = avatar.title;
		} else {
			avatar.innerHTML = '';
		}
	}

	air.trace('image:' + (image ? 'OK' : 'NG') + ' info:' + (postInfo ? 'OK' : 'NG') + ' ctrl:' + (postControls ? 'OK' : 'NG') +' avatar:' + avatar);

	if (image) {
		air.trace('Check post['+(no+1-0)+']: image');
	} else {
		air.trace('Check post'+(no+1-0)+': other');
	}

	// 画像のpostだけためこむ
	if (image && postControls) {
		// post_infoのリンクtargetを変更するよ
		if (postInfo) {
			var a = postInfo.getElementsByTagName('a');
			for (var x=0; x<a.length; x++) {
				a[x].target = '_blank';
			}
		}

		// 同じユーザーが連続してpostしてる時
		// postInfoは空になるから、かわりにavatar
		if (postInfo && postInfo.innerHTML) {
			postInfo = postInfo.innerHTML;
		} else if (avatar) {
			postInfo = '<a href="' + avatar.href + '" target="_blank">' + avatar.innerHTML + '</a>:';
		} else {
			postInfo = '';
		}

		tPod.posts.push({
			postInfo: postInfo,
			image: image,
			postControls: postControls
		});
	}

}

// 画像をぴこぴこ切り替えるやつ
function changer(noEffect) {
	tPod.elm.btNext.className = 'busy';
	tPod.elm.btNext.removeEventListener('click', hNext, false);
	tPod.elm.btBack.className = 'busy';
	tPod.elm.btBack.removeEventListener('click', hBack, false);

	air.trace('start image:'+tPod.count + '/' + tPod.posts.length);
	if (tPod.count >= tPod.posts.length) {
		tPod.count = 0;
	}

	// 表示部が崩れて正方形じゃなくなってたらなおす
	if (tPod.elm.view.offsetWidth != tPod.elm.view.offsetHeight) {
		hResize();
	}

	//var src = tPod.images[tPod.count].replace('src="', '');
	var src = tPod.posts[tPod.count].image.src;
	var img = new Image();
	img.src = src;
	img.id = 'mainImage';
	img.className = 'mainImage';

	// 画像の幅を取得したいからonloadまで待つよ
	img.addEventListener('load', function() {

		// imgのwidthとかheight調整するよ
		adjustImage(img);
		img.style.display = 'none';

		var oldImg = tPod.elm.view.getElementsByTagName('img')[0];

		// 何かの間違いでoldImgが無かった時はロゴでも入れとく
		if (!oldImg) {
			oldImg = new Image();
			img.src = 'img/tpod.png';
			tPod.elm.view.appendChild(oldImg);
		}

		if (tPod.elm.btMain.className == 'load') {
			tPod.elm.btMain.className = 'pause';
		}

		// post_info表示するよ
		var str = tPod.posts[tPod.count].postInfo || '';
		info(str + ' [' + (Number(tPod.count + 1)) + '/' + tPod.posts.length + ']');

		air.trace('show image:'+tPod.count);
		// 画像表示(切り替え)するよ
		new Effect.Appear(oldImg, { 
			from: 0.9,
			to: 0.1,
			delay: 0, // 開始までの秒数 
			fps: 60,
			duration: noEffect ? 0.1 : 0.3,
			beforeStartInternal: function(effect) {
			}, 
			afterFinishInternal: function(effect) { 
				tPod.elm.view.style.background = '#000';
				oldImg.removeEventListener('click', reblog, false);

				// ここで画像が差し替わる
				tPod.elm.view.replaceChild(img, oldImg);
				new Effect.Appear(img, { 
					from: 0.1,
					to: 1.0,
					delay: 0, // 開始までの秒数 
					fps: 60,
					duration: noEffect ? 0.1 : 0.6,
					beforeStartInternal: function(effect) {
					},
					afterFinishInternal: function(effect) {
						tPod.elm.btNext.addEventListener('click', hNext, false);
						tPod.elm.btNext.className = '';
						tPod.elm.btBack.addEventListener('click', hBack, false);
						tPod.elm.btBack.className = '';
						img.addEventListener('click', reblog, false);
					} 
				});
			} 
		}); // End of Effect

		tPod.count++;
	}, false); // End of img.onload
}

// 画面の幅にあわせて比率を崩さないように
// imgのwidthとかheight調整するよ
function adjustImage(img) {
	// 表示部の幅
	var vw = tPod.elm.view.offsetWidth;
	var vh = tPod.elm.view.offsetHeight;

	// 画像の幅
	var iw = img.naturalWidth;
	var ih = img.naturalHeight;

	// 最終的にimgにセットするwidthとheight
	var w = '';
	var h = '';

	if ( (vw < iw) && (vh < ih) ) {
		if (iw < ih) {
			h = vh;
		} else {
			w = vw;
		}
	} else {
		if (vw < iw) {
			w = vw;
		} else if (vh < ih) {
			h = vh;
		}
	}

	air.trace('v:' + vw + 'x' + vh + ' i:' + iw + 'x' + ih + ' r:' + (w ? w : '(null)')  + 'x' + (h ? h : '(null)'));
	if (w) {
		img.width = w-2;
	}

	if (h) {
		img.height = h-2;
	}
}


// リブログ機能! 超かっこいいインターフェースで! って思ったけど
// なんかもうめんどくさいからリンクひらくだけでいいや
function reblog() {
	// クリックのタイミングによってはうまくいかないかも?
	// requestTimerでgetDashboard動いちゃって、tPod.postsが空だとか。

	// air.trace(tPod.posts[tPod.count].postControls);

	var c = tPod.count - 1;
	if (c < 0) {
		c = tPod.count.length - 1;
	}

	var a = tPod.posts[c].postControls.getElementsByTagName('a');

	var href;
	for (var i=0; i<a.length; i++) {
		if (a[i].href.match(/reblog/)) {
			href = a[i].href;
			break;
		}
	}

	if (href) {
		open(tPod.cfg.tumblrRoot + href, null, 'menubar=yes,toolbar=yes,location=yes,resizable=yes,scrollbars=yes');
	}
}

// ログインしてるかチェックするやつ
// 一応post用のkeyも取得しておくよ
function checkLogin() {
	tPod.elm.loginStatus.className = '';

	air.trace('check login ...');

	var temp = tPod.elm.loginStatus.innerHTML;
	tPod.elm.loginStatus.innerHTML = '<span class="check">[Check login]</span>';

	new Ajax.Request(
		tPod.cfg.tumblrSave, {
			method: 'get', 
			onComplete: function(xhr) {
				if (xhr.responseText.match(/name="form_key"[^>]+value="([^"]+)"/i)) {
					tPod.formKey = RegExp.$1;
					tPod.login = true;
					//tPod.elm.loginStatus.style.display = 'none';
					//air.trace(tPod.formKey);
					tPod.elm.loginStatus.innerHTML = '[<a href="http://www.tumblr.com/dashboard" target="_blank">&raquo;Dashboard</a>]';
					tPod.elm.loginStatus.style.display = 'block';
					//debug(xhr.responseText);
				} else {
					tPod.login = false;
					tPod.elm.loginStatus.innerHTML = '[Not logined &#8594; <a href="http://www.tumblr.com/login" target="_blank">Login</a>]';
					tPod.elm.loginStatus.style.display = 'block';
				}
			},
			onFailure: function(xhr) {
				tPod.elm.loginStatus.innerHTML = temp;
			}
		}
	);

	if (tPod.checkLoginTimer) {
		clearTimeout(tPod.checkLoginTimer);
	}

	var t = tPod.login ? tPod.cfg.checkLoginInterval : tPod.cfg.checkLoginInterval_nologin;
	tPod.checkLoginTimer = setTimeout(checkLogin, t);
}

// バージョンチェック
function checkVersion() {
	air.trace('check version ...');
	info('Checking version ...');

	new Ajax.Request(
		tPod.cfg.versionCheckPage, {
			method: 'get', 
			onComplete: function(xhr) {
				if (xhr.responseText.match(/^v([0-9a-zA-Z.]+)$/i)) {
					if (tPod.version < RegExp.$1) {
						if (confirm('New version v' + RegExp.$1 + ' available. (this version is v' + tPod.version + ')\nDo you want to open download page?')) {

							// 標準ブラウザでダウンロードページ開くよ
							air.navigateToURL(new air.URLRequest(tPod.cfg.installPage), "_blank");
							// tPod終了するよ
							saveInit();
							nativeWindow.close();
						}
					}
				}
				info('');
			},
			onFailure: function(xhr) {
				info('');
			}
		}
	);
}

// デバッグログ
function debug(str) {
	if (tPod.cfg.debug) {
		var d = $('log');
		d.value = str + '\n' + d.value;
	}
}

ot_incot_inc 2009/03/15 13:34 自分のTumblrが出てていたので吹いた件