またニコニコ動画見てるグリモンをSeaHorseに移植したい

とりあえずイベント処理やflvplayerの取得などをIEコンポーネント対応するのは出来て、
GM_xmlhttpRequestのダミーで本来POSTすべき内容をalertで表示するところまでは出来た。

// ==UserScript==
// @name      mata nicovideo
// @namespace http://d.hatena.ne.jp/monjudoh/
// @include   http://www.nicovideo.jp/watch/*
// @author    monjudoh
// @description  Original Greasemonkey script : http://muumoo.jp/news/2008/01/28/0matanicovideo.html
// @version   0.2
// ==/UserScript==

(function(){
	function GM_xmlhttpRequest(params){
		alert('url:' + params.url + '\ndata:' + params.data);
	}
	Number.prototype.z = function(len){
		var s = '0'.fill(len) + this.toString();
		return s.substr(s.length - len);
	};
	String.prototype.fill = function(len){
		var result = '';
		for(var i = 0; i < len; i++) result += this;
		return result;
	};

	var h1 = document.getElementsByTagName('h1')[0];
	if(!h1) return;
	var span = document.createElement('span');
	h1.parentNode.insertBefore(span, h1);
	
	var a = document.createElement('a');
	a.appendChild(document.createTextNode('[\u307e\u305f\u898b\u3066\u308b]'));
	a.href = 'javascript:void(0);';
	a.attachEvent('onclick',
		function(){
			var srcElem = window.event.srcElement;
			srcElem.parentNode.removeChild(srcElem);
			
			var text = document.createElement('input');
			text.type = 'text';
			text.value = '';
			text.size = 20;
			text.setAttribute('maxlength', 10);
			
			var a = document.createElement('a');
			a.appendChild(document.createTextNode('[\u307e\u305f\u898b\u3066\u308b!]'));
			a.href = 'javascript:void(0);';
			a.attachEvent('onclick',
				function(){
					var subject = h1.getElementsByTagName('a')[0].innerHTML;
					var url = location.href;
					
					var flvplayer = document.getElementById('flvplayer');
					
					var len = flvplayer.GetVariable('ContentLength');
					var lmin = Math.floor(len / 60);
					var lsec = len % 60;
					
					GM_xmlhttpRequest({
						method: 'POST',
						url: 'http://twitter.com/statuses/update.json',
						headers: { 'Content-type': 'application/x-www-form-urlencoded' },
						data: 'status=' + encodeURIComponent(
							'\u307e\u305f\u30cb\u30b3\u30cb\u30b3\u52d5\u753b\u898b\u3066\u308b : ' +
							subject + ' ' + url + ' (' + lmin.z(2) + ':' + lsec.z(2) + ')' +
							((0 < text.value.length) ? ' ' + text.value : '')
						),
						onload: function(res){ GM_log('\u6295\u7a3f\u3057\u307e\u3057\u305f\u3002'); },
						onerror: function(res){ GM_log(res.status + ':' + res.statusText); }
					});
					var srcElem = window.event.srcElement;
					srcElem.parentNode.parentNode.removeChild(srcElem.parentNode);
				});
			span.appendChild(a);
			span.appendChild(text);
			text.focus();
		});
	span.appendChild(a);
})();

GM_xmlhttpRequestの代わりはどうするかというと、
iframeを使用したクロスドメインPOSTでできるんじゃないかと高をくくってた。
実際APIにPOSTするところまでは上手くいったものの、↓こんなエラーメッセージが帰ってきた。

Sorry, due to abusive behaviour we have been forced to disable posting from external websites. If you are posting from an API tool please ensure that the HTTP_REFERER header is not set.

src属性を設定しないでこしらえてやったiframeの中のformからPOSTすると、
リファラがiframeを持ってる画面のURLになってしまうようなのだ。
で、twitterの投稿APIリファラがよそのサイトになってると投稿を受け付けないので失敗してしまった。
ということなのでした。


次回はこの辺を参考にしてみようかと思う。

iframeを使用したクロスドメインPOST

指定したidのiframeのdocumentを取得する関数

function getIframeDoc(iframeId){
	var iframe = document.getElementById(iframeId);
	if (document.all) {
		return iframe.contentWindow.document;
	} else {
		return iframe.contentDocument;
	}
}

こんな感じで適当なdivを作って適当なiframeを放り込んで、body作ってDOM操作できるようにしてやる。

var div = document.createElement('div');
div.id = 'iframeContainer';
document.body.appendChild(div);

var iframe = document.createElement('iframe');
iframe.id = 'iframe';
document.getElementById('iframeContainer').appendChild(iframe);

var iframedoc = getIframeDoc('iframe');
iframedoc.writeln('<body></body>');
iframedoc.close();

iframe経由でPOSTしてやる関数

まあ見たまんま

function submit(iframedoc,action,params){
	var form = iframedoc.createElement('form');
	form.method = 'post';
	form.action = action;
	iframedoc.body.appendChild(form);
	for(var paramName in params){
		var paramValue = params[paramName];
		var input = iframedoc.createElement('input');
		input.type = 'text';
		input.name = paramName;
		input.value = paramValue;
		form.appendChild(input);
	}
	form.submit();
}

空iframeを使った場合は基本的にparamsはUTF-8でURLエンコードされてPOSTされるけど、
documentのinputEncodingプロパティが他のエンコーディング(EUC-JPとかSJISとか)になってるページを
読み込ませたiframeだったら、そのエンコーディングでURLエンコードされてPOSTされるよ。
(↑クロスドメインの時はダメだよ)
クロスドメインじゃなければ↓みたいな感じでレスポンスも取れるけど、↑と併用しないとそんなにうれしくないね。

getIframeDoc('iframe').body.innerHTML;


ずっとFirebugで弄ってたんでFirefox以外での動作には自信ないです。
ちゃっちゃとIEコンポーネントブラウザでの動作も確認して、
クロスドメインでPOSTする&レスポンス情報は別になくても困らない系の用途で
GM_xmlhttpRequestを使っているグリモンをSeaHorseに移植したい。