/********************************************************************************

	フィーダー
		作成者	Masashi Kikkawa

	【説明】
		複数のフィードをマージすることも可能。
		各種設定は「feeder_config.js」に記述。

	【更新履歴】
		2009.08.05 v.1.04
			フィーダー取得時の原因不明エラーの警告が出ないように変更。
		2009.07.21 v.1.03
			RSS1.0の場合、Safariで正常に判定できなかったバグを修正。
		2009.05.26 v.1.02
			Atomに対応。
			一部のRSSで取得できなかった不具合を修正。
		2009.04.09 v.1.01
			外部CGIアダプタを用いてドメインをまたぐフィードが取得できるように改良。
		2008.07.29 v.1.00
			公開。

********************************************************************************/




//_______________________________________________________________________________
//	フィーダー
var	eoFeeder	=	new class_eoFeeder();
function	class_eoFeeder()
{
	this.objOnload	=	window.onload;	//	既存のonloadイベント

	//	変数
	this.aRequest		=	[];	//	HttpRequestオブジェクト一覧
	this.aFeederIDList	=	[];	//	読み込むフィーダーID一覧
	this.aFeederList	= 	[];	//	読み込むフィーダー一覧
	this.vAdapter		=	'';	//	フィードの取り込みに使うアダプタ（なければjsの機能だけで接続）

	//___________________________________________________________________________
	//	フィーダー初期化
	this.Init	=	function()
	{
		//	既存のonLoadイベントを実行
		if( this.objOnload != null )	this.objOnload();

		//	各ID別に処理
		for( var i=0; i<this.aFeederIDList.length; i++ )
		{
			//	フィーダーIDチェック
			if( !document.getElementById( this.aFeederIDList[i] ) )
			{
				this._Alert( 'Init', 'フィーダーID ' + this.aFeederIDList[i] + ' がHTML上に見つかりません。' );
			}
			else
			{
				//	各フィードを読み込み
				for( var j=0; j<this.aFeederList[this.aFeederIDList[i]].aFeedList.length; j++ )
				{
					this._LoadFeed( this.aFeederIDList[i], this.aFeederList[this.aFeederIDList[i]].aFeedList[j] );
				}
			}
		}
	};

	//___________________________________________________________________________
	//	アダプタをセット
	this.SetAdapter	=	function( vAdapterSrc )
	{
		this.vAdapter	=	vAdapterSrc;
	};

	//___________________________________________________________________________
	//	フィーダーをセット
	this.SetFeeder	=	function( vID, vEntryCnt, aFeedList, funcCallback )
	{
		this.aFeederIDList[this.aFeederIDList.length]	=	vID;
		this.aFeederList[vID]							=	new class_eoFeed( vEntryCnt, aFeedList, funcCallback );
	};

	//___________________________________________________________________________
	//	フィードの読み込み
	this._LoadFeed	=	function( vID, vRss )
	{
		var	vReqIdx	=	this.aRequest.length;

		this.aRequest[vReqIdx]	=	this._createXMLHttpRequest( vID, vReqIdx, this._getXML );

		if( this.aRequest[vReqIdx] )
		{
			if( this.vAdapter != '' )
			{
				this.aRequest[vReqIdx].open( 'GET', this.vAdapter + '?' + vRss, true );
			}
			else
			{
				this.aRequest[vReqIdx].open( 'GET', vRss, true );
			}
			this.aRequest[vReqIdx].send( null );
		}
	};

	//___________________________________________________________________________
	//	読み込み完了処理
	this._Loaded	=	function( vID, xmlRes )
	{
		var	vFeedType	=	'';
		var	vEntries	=	'';

		//	フィードの種類を取得
		if( xmlRes.getElementsByTagName( 'rdf:RDF' )[0] )
		{	//	RSS1.0
			vFeedType	=	'RSS' + xmlRes.getElementsByTagName( 'rdf:RDF' )[0].getAttribute( 'xmlns' ).match( /http:\/\/purl.org\/rss\/([0-9.]+)\// )[1];
		}
		else if( xmlRes.getElementsByTagName( 'RDF' )[0] )
		{	//	RSS1.0（Safariの場合）
			vFeedType	=	'RSS' + xmlRes.getElementsByTagName( 'RDF' )[0].getAttribute( 'xmlns' ).match( /http:\/\/purl.org\/rss\/([0-9.]+)\// )[1];
		}
		else if( xmlRes.getElementsByTagName( 'rss' )[0] )
		{	//	RSS2.0
			vFeedType	=	'RSS' + xmlRes.getElementsByTagName( 'rss' )[0].getAttribute( 'version' );
		}
		else if( xmlRes.getElementsByTagName( 'feed' )[0] )
		{	//	Atom
			vFeedType	=	'Atom';
		}
		
		//	フィードを取得
		switch( vFeedType )
		{
		case	'RSS1.0':
		case	'RSS2.0':
			vEntries	=	xmlRes.getElementsByTagName( 'item' );
			break;
		case	'Atom':
			vEntries	=	xmlRes.getElementsByTagName( 'entry' );
			break;
		}
		
		//	フィードをパース
		for( var i=0; i<vEntries.length && i<this.aFeederList[vID].vEntryCnt; i++ )
		{
			var	vTitle		=	'';
			var	vLink		=	'';
			var	vContent	=	'';
			var	vDate		=	'';
			var	vCtgry		=	'';
			var	vCreator	=	'';

			//	各値を取得
			for( var j=0; j<vEntries[i].childNodes.length; j++ )
			{
				//	データの有無をチェック
				switch( vFeedType )
				{
				case	'RSS1.0':	//	RSS1.0の場合
					switch( vEntries[i].childNodes[j].nodeName )
					{
					case	'title':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vTitle	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'link':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vLink	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'content:encoded':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vContent	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'dc:date':
						if( vEntries[i].childNodes[j].firstChild )
						{
							var	aTmpDCDate	=	vEntries[i].childNodes[j].firstChild.nodeValue.match( /^([0-9-]+)T([0-9:]+)+|-/ );
							var	aTmpDate	=	aTmpDCDate[1].split( '-' );
							var	aTmpTime	=	aTmpDCDate[2].split( ':' );
							//	ブランクチェック
							for( var k=0; k<3; k++ )
							{
								if( !aTmpDate[k] )	aTmpDate[k]	=	0;
								if( !aTmpTime[k] )	aTmpTime[k]	=	0;
							}
							vDate	=	new Date( aTmpDate[0], aTmpDate[1]-1, aTmpDate[2], aTmpTime[0], aTmpTime[1], aTmpTime[2] );
						}
						break;
					case	'dc:subject':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vCtgry	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'dc:creator':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vCreator	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					}
					break;
				case	'RSS2.0':	//	RSS2.0の場合
					switch( vEntries[i].childNodes[j].nodeName )
					{
					case	'title':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vTitle	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'link':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vLink	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'description':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vContent	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'pubDate':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vDate	=	new Date( vEntries[i].childNodes[j].firstChild.nodeValue );
						}
						break;
					case	'dc:subject':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vCtgry	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'dc:creator':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vCreator	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					}
					break;
				case	'Atom':	//	Atomの場合
					switch( vEntries[i].childNodes[j].nodeName )
					{
					case	'title':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vTitle	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'link':
						vLink	=	vEntries[i].childNodes[j].getAttribute( 'href' );
						break;
					case	'content':
						if( vEntries[i].childNodes[j].firstChild )
						{
							vContent	=	vEntries[i].childNodes[j].firstChild.nodeValue;
						}
						break;
					case	'published':
						if( vEntries[i].childNodes[j].firstChild )
						{
							var	aTmpDCDate	=	vEntries[i].childNodes[j].firstChild.nodeValue.match( /^([0-9-]+)T([0-9:]+)+|-/ );
							var	aTmpDate	=	aTmpDCDate[1].split( '-' );
							var	aTmpTime	=	aTmpDCDate[2].split( ':' );
							//	ブランクチェック
							for( var k=0; k<3; k++ )
							{
								if( !aTmpDate[k] )	aTmpDate[k]	=	0;
								if( !aTmpTime[k] )	aTmpTime[k]	=	0;
							}
							vDate	=	new Date( aTmpDate[0], aTmpDate[1]-1, aTmpDate[2], aTmpTime[0], aTmpTime[1], aTmpTime[2] );
						}
						break;
					case	'category':
						vCtgry	=	vEntries[i].childNodes[j].getAttribute( 'label' );
						break;
					case	'author':
						vCreator	=	vEntries[i].childNodes[j].getElementsByTagName( 'name' )[0].firstChild.nodeValue;
						break;
					}
					break;
				}
			}

			//	エントリをストック
			this.aFeederList[vID].aLoadEntry[this.aFeederList[vID].aLoadEntry.length]	=	new class_eoEntry( vTitle, vLink, vContent, vDate, vCtgry, vCreator );
		}

		//	読み込み完了フィード数をカウント
		this.aFeederList[vID].vLoadCnt	+=	1;

		//	全フィード読み込み判定
		if( this.aFeederList[vID].vLoadCnt < this.aFeederList[vID].aFeedList.length )
		{	//	リスト分読み込んでなければ終了
			return;
		}

		//	各エントリを更新日時で降順ソート
		for( var i=0; i<this.aFeederList[vID].aLoadEntry.length-1; i++ )
		{
			for( var j=0; j<this.aFeederList[vID].aLoadEntry.length-1-i; j++ )
			{
				if( this.aFeederList[vID].aLoadEntry[j].vDate < this.aFeederList[vID].aLoadEntry[j+1].vDate )
				{
					var	vEntryTmp						=	this.aFeederList[vID].aLoadEntry[j];
					this.aFeederList[vID].aLoadEntry[j]	=	this.aFeederList[vID].aLoadEntry[j+1];
					this.aFeederList[vID].aLoadEntry[j+1]	=	vEntryTmp;
				}
			}
		}

		//	コールバックの実行
		if( this.aFeederList[vID].funcCallback )	this.aFeederList[vID].funcCallback( vID, this.aFeederList[vID].aLoadEntry );
	};

	//___________________________________________________________________________
	//	警告表示
	this._Alert	=	function( vMethodName, vMsg )
	{
		alert( '【フィーダーエラー】\n（' + vMethodName + '）\n\n' + vMsg );
	};

	//_______________________________________________________________________________
	//	XMLを取得
	this._getXML	=	function( vID, vReqIdx )
	{
		if( eoFeeder.aRequest[vReqIdx].readyState == 4 )
		{
			switch( eoFeeder.aRequest[vReqIdx].status )
			{
			case 200:
				if( eoFeeder.aRequest[vReqIdx].responseXML )
				{
					eoFeeder._Loaded( vID, eoFeeder.aRequest[vReqIdx].responseXML );
				}
				else
				{
					eoFeeder._Alert( '_getXML', 'XMLが取得できません。' );
				}
				break;
			case 403:
				eoFeeder._Alert( '_getXML', 'アクセスが拒否されました。' );
				break;
			case 404:
				eoFeeder._Alert( '_getXML', 'ファイルがありません。' );
				break;
			default:
//				eoFeeder._Alert( '_getXML', '原因不明のエラーが発生しました。（' + eoFeeder.aRequest[vReqIdx].status + '）' );
				break;
			}
		}
	};

	//_______________________________________________________________________________
	//	HttpRequestオブジェクトを生成
	this._createXMLHttpRequest	=	function( vID, vReqIdx, funcGetXML )
	{
		var	XMLhttpObject	=	null;

		try
		{
			XMLhttpObject	=	new XMLHttpRequest();
		}
		catch( e )
		{
	 		try
			{
				XMLhttpObject	=	new ActiveXObject( 'Msxml2.XMLHTTP' );
			}
			catch( e )
			{
				try
				{
					XMLhttpObject	=	new ActiveXObject( 'Microsoft.XMLHTTP' );
				}
				catch( e )
				{
					return	null;
				}
			}
		}

		if( XMLhttpObject )	XMLhttpObject.onreadystatechange	=	function(){ funcGetXML( vID, vReqIdx ) };

		return	XMLhttpObject;
	};

	//___________________________________________________________________________
	//	フィードクラス
	function	class_eoFeed( vEntryCnt, aFeedList, funcCallback )
	{
		this.vEntryCnt		=	vEntryCnt;
		this.aFeedList		=	aFeedList;
		this.funcCallback	=	funcCallback;
		this.aLoadEntry		=	[];
		this.vLoadCnt		=	0;
	};

	//___________________________________________________________________________
	//	エントリクラス
	function	class_eoEntry( vTitle, vLink, vContent, vDate, vCtgry, vCreator  )
	{
		this.vTitle		=	vTitle;
		this.vLink		=	vLink;
		this.vContent	=	vContent;
		this.vDate		=	vDate;
		this.vCtgry		=	vCtgry;
		this.vCreator	=	vCreator;
	};
}




//_______________________________________________________________________________
//	window.onloadにセット
window.onload	=	function()
{
	//	フィーダー初期化
	eoFeeder.Init();
}




