/*

		Preferred Style Generator (PSG)
		2005-01-18 : v3.3.1 Stylesheet Only
		Written by TYLIGHT
		
		この JavaScript は修正 BSD ライセンスで配布します。

*/

/* エラーを無視 (参考: http://east.portland.ne.jp/~sigekazu/css/javascript10.htm ) */
function PSGDummyFunction(){
	return true;
}
window.onerror = PSGDummyFunction;

var PSG = {
	/******************* 設定ここから *******************/
	Config : {
		Cookie : {
			// クッキーが有効なドメイン名を指定します。空だとURLから自動的に取得します (String)
			// 例: "homepage2.gifty.com"
			domain : "",
			
			// "/" から始まるクッキーが有効なパスを指定します。空だとURLから自動的に取得します (String)
			// 例: "/oreore/"
			path : ""
		},
		StyleSheet : {
			// どの ID を持つ要素にセレクトボックスを配置するか (String)
			// 空だと、次の要素名の設定を使って配置します
			targetElementID : "",
			
			// どの要素にセレクトボックスを配置するか (String)
			// 上の設定で ID を指定した場合は無視されます
			targetElementName : "body",
			
			// その要素の何番目に挿入するか。0 ではなく 1 から数えます。(int)
			// 上の設定で ID を指定した場合は無視されます
			targetElementIndex : 1,
			
			// 選択リストの直前に挿入する文字列。空でもいい (String)
			text : "Theme ",
			
			// 上で設定した要素の前に挿入するなら true, 後ろに追加するなら false
			isBefore : false,
			
			// 上で指定した要素の外に追加するなら true
			// 要素の中に追加するなら false
			// body 要素や html 要素を挿入対象にする場合は false にしてください。
			isOut : false,
			
			// 設定スタイルの保持日数(一度でも再来訪すればまた数え直します) (int)
			// 0 を指定するとブラウザ終了まで有効。
			expdays : 30,
						
			// 優先/代替 CSS 全て適用しない状態を選択リストに含めるなら true, 含めないなら false
			enableNoStyle : true,
			
			// 優先/代替 CSS 一切適用無しの名前 (String)
			noStyleName : "None"
		}
	},
	/******************* 設定ここまで *******************/






	Arrays : {
		/*
		@access : public
		@param array : object : 検索対象配列
		@param value : mixed :  検索するオブジェクト
		@return : boolean : 対象配列に既にオブジェクトが存在していれば true, そうでなければ false
		@description :
		配列の値の重複チェックなどに使用。
		hasKey も作るかもしれないので has でなく hasValue という名前。
		*/
		hasValue : function( array, value ){
			for(var i in array){
				if(array[i] == value){
					return true;
				}
			}
			return false;
		}
	},



	DOM : {
		namespace : {
			xhtml1 : "http://www.w3.org/1999/xhtml",
			xhtml2 : "http://www.w3.org/2002/06/xhtml2"
		},
		
		/*
		@access : public
		@return : object(HTMLHtmlElement) : ページ内のルート要素 or null
		*/
		getRootElement : function(){
			/* document.all(0) が要素とは限らない為 document.all 非使用 [Feedback 2004-12-19] */
			if(document.documentElement){
				return document.documentElement;
			} else if (document.getElementsByTagName){
				return document.getElementsByTagName("*")[0];
			} else {
				return null;
			}
		},
		
		/*
		@access : public
		@return : boolean : 最低限の DOM が扱えるかどうか
		*/
		canDOM : function(){
			var D = document;
			var E = PSG.DOM.getRootElement();
			if( E == null ) return false;
			return !!(
				D.getElementById &&
				D.getElementsByTagName &&
				D.createElement &&
				D.createTextNode &&
				E.getAttribute &&
				E.setAttribute &&
				E.removeAttribute &&
				E.appendChild &&
				E.removeChild &&
				E.insertBefore
			);
		},
		
		/*
		@access : public
		@return : boolean : 文書が XHTML かどうか
		*/
		isXHTML : function(){
			if( PSG.DOM.canDOM() == false ) return false;
			
			var root = PSG.DOM.getRootElement();
			/*
			XHTML/1.1 はルート要素に xmlns 属性が定義されなければならない
			HTML なら namespaceURI は null
			Opera7 は getAttribute では xmlns 属性が取得できない (getAttributeNode なら Object が返る)
			WinIE6 は namespaceURI プロパティが存在しない
			*/
			return !!( root.getAttribute("xmlns") || root.namespaceURI );
		},
		
		/*
		@access : public
		@param tagName : 生成する要素名
		@return : object(HTML*Element) : 生成された要素
		@description :
		XHTML / HTML のどちらであるかを自動的に判断して指定要素を生成します。
		XHTML の場合に生成される要素の名前空間は XHTML/1.x 共通のものです。
		*/
		createElement : function( elementName ){
			elementName = elementName.toLowerCase();
			return PSG.DOM.isXHTML() && document.createElementNS ? document.createElementNS(PSG.DOM.namespace.xhtml1, elementName) : document.createElement(elementName);
		},
		
		/*
		@access : public
		@param newNode : object(DOMNode) : refNode に追加するノード
		@param refNode : object(DOMNode) : ノードが追加されるノード
		@return : object/null : 自動補完が加わったかもしれない newNode
		@description :
		対象のノードが DL,UL,OL のいずれかであれば newNode に適切に要素を加えて返します。
		対象ノードが null か DOM が扱えなければ null を返します。
		*/
		addElementImpl : function( newNode, refNode ){
			if( PSG.DOM.canDOM() == false || !refNode ) return null;
			
			/* Feedback from ALIMIKA SATOMI 2004-12-19 */
			var regxp = new RegExp("^(HTML:)?(UL|OL|DL)$", "i"); // XHTML [Opera7] 対策
			if( regxp.test(refNode.nodeName) ){
				var parentElement;
				if(refNode.nodeName.toUpperCase().indexOf("DL") != -1 ){
					parentElement = PSG.DOM.createElement("dd");
				} else {
					parentElement = PSG.DOM.createElement("li");
				}
				if( newNode.id ){
					parentElement.id = newNode.id + "_PARENT";
				}
				parentElement.appendChild(newNode);
				return parentElement;
			} else {
				return newNode;
			}
		},
		
		/*
		@access : public
		@param newElement : 新しく追加する要素
		@param targetElement : 追加対象の要素
		@param isBefore : 追加対象の要素の前に追加するのかどうか (後ろなら false)
		@param isOut : 追加対象要素の外に追加するのかどうか (中なら false)
		@return : object : 追加した newElement
		@description :
		新しい要素を追加対象要素の前後どちらかに追加します。
		追加対象要素が UL/OL/DL のいずれかである場合は適切に要素を補充した後に
		それを新しい要素として追加します。返り値はここで生成された新しい要素です。
		UL/OL/DL ではなければそのまま追加し、返り値の要素も最初の新しい要素と変化しません。
		
		DOM をサポートしていない場合は何もせずに null を返します。
		*/
		addElement : function( newElement, targetElement, isBefore, isOut ){
			if( PSG.DOM.canDOM() == false ) return null;
			
			if( isBefore ){
				if( isOut ){
					newElement = PSG.DOM.addElementImpl( newElement, targetElement.parentNode );
					targetElement.parentNode.insertBefore( newElement, targetElement );
				} else {
					newElement = PSG.DOM.addElementImpl( newElement, targetElement );
					targetElement.insertBefore( newElement, targetElement.firstChild );
				}
			} else {
				if( isOut ){
					newElement = PSG.DOM.addElementImpl( newElement, targetElement.parentNode );
					targetElement.parentNode.insertBefore( newElement, targetElement.nextSibling );;
				} else {
					newElement = PSG.DOM.addElementImpl( newElement, targetElement );
					targetElement.appendChild( newElement );
				}
			}
			return newElement;
		}
	},



	Event : {
		/*
		@access : public
		@param object : イベントを登録する要素
		@param event : イベント名
		@param listener : イベント発生時に自動実行する関数名
		@param useCapture : キャプチャするかどうからしい……
		@return : void
		@description :
		イベントの登録を行う。
		*/
		addEvent : function( object, event, listener, useCapture ){
			if(object.addEventListener){
				object.addEventListener( event, listener, useCapture ? true : false ); // Standard DOM
			} else if(object.attachEvent){
				object.attachEvent( "on"+event, listener ); // For IE
			}
		},
		
		/*
		@access : public
		@param listener : ページ読み込み完了時に自動実行する関数名
		@return : void
		*/
		addLoadEvent : function( listener ){
			PSG.Event.addEvent( window, "load", listener, false );
		}
	},



	Cookie : {
		/*
		@access : public
		@param name : 取得するクッキーの名前
		@return : クッキーが存在しなければ null、そうでなければクッキーの値
		*/
		getCookie : function(name){
			var start, end;
			var c = document.cookie;
			
			if(c.indexOf(name + '=') != -1){
				start = c.indexOf(name + '=');
				end = c.indexOf( ';', start + name.length+1 );
				if(end == -1){
					end = c.length;
				}
				var value = c.substring( start + name.length+1, end );
				return decodeURIComponent ? decodeURIComponent(value) : unescape(value) ;
			}
			return null;
		},
	
		/*
		@access : public
		@param name : 設定するクッキーの名前
		@param value : 設定する値
		@param expdays : クッキー有効日数
		@param domain : クッキーを有効にするドメイン
		@param path : クッキーを有効にするパス
		@return : void
		*/
		setCookie : function(name,value,expdays,domain,path){
			if( value == null ) throw "Invalid Value";
			if( !expdays ) expdays = 0; // -1 だと Firefox/1.0 ではその場で削除されるようだ
			if( !domain ) domain = location.hostname; // 空設定等の場合の対処
			if( !path ) path = location.pathname.substring(0,location.pathname.lastIndexOf("/")+1 ); // 空設定等の場合の対処
			
			value = encodeURIComponent ? encodeURIComponent(value) : escape(value) ;
			
			var c = name + "=" + value;
			c += "; domain=" + domain;
			c += "; path=" + path;
			if(expdays != 0){
				var expires = new Date();
				expires.setTime(expires.getTime() + 1000*60*60*24*expdays);
				c += "; expires=" + expires.toGMTString();
			}
			document.cookie = c;
		}
	},



	StyleSheet : {
		selectID : "PSGSSSBOX", // セレクトボックスに付けるID。内部的にも使用するので重要
		formID : "PSGSSS", // PSG Style Sheet Selecter の略。フォーム要素の ID。CSS で制御しやすいように付けている
		
		/*
		@access : protected
		@return : 優先/代替スタイルシートに関連している styleSheets オブジェクト(それが利用できなければ link 要素)の配列
		@description :
		styleSheets オブジェクトから title があるものだけを返します。
		styleSheets オブジェクトが利用できない場合は代わりに link 要素から取得します。
		返り値に永続スタイルシートは含みません。
		link 要素での取得の場合に DOM がサポートされていなければ空配列を返します。
		*/
		getStyleSheets : function(){
			var i;
			var sheets = new Array();
			if( document.styleSheets ){
				var csses = document.styleSheets;
				for(i=csses.length-1; i>=0; i--){
					if( csses[i].title ){
						sheets.unshift(csses[i]);
					}
				}
			} else {
				if( PSG.DOM.canDOM() == false ) return new Array();
				
				var objLinks = document.getElementsByTagName("link");
				for(i=objLinks.length-1; i>=0; i--){ // この場合は for(var i in objLinks) は正常動作しません！
					if(
					objLinks[i].getAttribute("rel") &&
					objLinks[i].getAttribute("rel").toLowerCase().indexOf("stylesheet") != -1 &&
					objLinks[i].getAttribute("title") ){
						sheets.unshift(objLinks[i]);
					}
				}
			}
			return sheets;
		},
		
		/*
		@access : public
		@param sheet : 適用するスタイルシート名
		@return : void
		@description :
		sheet と同一名のスタイルシートを有効に、それ以外を無効にします。
		永続スタイルシートには影響を及ぼしません。
		DOM がサポートされていなければ何もしません。
		*/
		changeStyleSheet : function(sheet){
			if( !sheet || PSG.DOM.canDOM() == false ) return;
			
			var sheets = PSG.StyleSheet.getStyleSheets();
			for(var i in sheets){
				/* WinIE6 は以下のようにするとページ読み込み時に何故か全てのスタイルを無効にする。Fxxk。
				sheets[i].disabled = (sheets[i].getAttribute("title") != sheet);
				*/
				sheets[i].disabled = true;
				var title = sheets[i].title;
				if( !title ) title = sheets[i].getAttribute("title");
				if(title == sheet){
					sheets[i].disabled = false;
				}
			}
			PSG.Cookie.setCookie("sheet", sheet, PSG.Config.StyleSheet.expdays, PSG.Config.Cookie.domain, PSG.Config.Cookie.path );
		},
		
		/*
		@access : protected?
		@return : void
		@description :
		選択スタイル名によるクラス名の再設定と実際の CSS の切り替えを行う。
		イベントで割り当てられて実行される。
		*/
		handleStyleSheet : function(){
			var selectElement = document.getElementById(PSG.StyleSheet.selectID);
			var sheet = selectElement.options[selectElement.selectedIndex].text;
			var options = selectElement.options;
			for(var i=options.length-1; i>=0; i--){
				options[i].className = (options[i].text == sheet) ? "enable" : "disable";
			}
			PSG.StyleSheet.changeStyleSheet(sheet);
		},
		
		
		/*
		@access : public
		@return : void
		@description :
		link 要素で指定されているスタイルシートのタイトルを一覧でセレクトボックスとして出力します。
		出力は div 要素に select 要素を含めた形で行われます。
		form 要素には PSG.StyleSheet.formID を付けます。
		select 要素には PSG.StyleSheet.selectID を付けます。
		このセレクトボックスは選択項目が変更されると
		自動的に適切な changeStyleSheet() を実行されるよう設定されます。
		また、現在有効なスタイルの項目は選択状態にされます。
		DOM がサポートされていなければ何もしません。
		*/
		createController : function(){
			if( PSG.DOM.canDOM() == false ) return;
			
			var selectForm = PSG.DOM.createElement("form");
			var div = PSG.DOM.createElement("div");
			var select = PSG.DOM.createElement("select");
			var sheet = PSG.Cookie.getCookie("sheet");
			var sheets = PSG.StyleSheet.getStyleSheets();
			
			selectForm.id = PSG.StyleSheet.formID;
			selectForm.setAttribute("action", location.href); // form 要素には action 属性が必須
			select.id = PSG.StyleSheet.selectID;
			
			/*
			同一の title 属性を持つスタイルシートの link が複数あると
			option が重複するためセレクトボックスに含める前にチェックします [Feedback 2004-12-19]
			*/
			var titles = new Array();
			for(var i in sheets){
				var title = sheets[i].title;
				if( !title ) title = sheets[i].getAttribute("title");
				if( PSG.Arrays.hasValue(titles, title) == false ){
					titles.unshift(title);
					var opt = PSG.DOM.createElement("option");
					opt.appendChild( document.createTextNode(title) );
					select.appendChild(opt);
					if( title == sheet ){
						select.selectedIndex = i;
						opt.className = "enable";
					} else {
						opt.className = "disable";
					}
				}
			}
			
			/* 優先/代替スタイル一切適用無し状態が設定で有効なら追加 */
			if( PSG.Config.StyleSheet.enableNoStyle && PSG.Config.StyleSheet.noStyleName ){
				var nostyle = PSG.DOM.createElement("option");
				nostyle.appendChild( document.createTextNode(PSG.Config.StyleSheet.noStyleName) );
				select.appendChild(nostyle);
				if( sheet == PSG.Config.StyleSheet.noStyleName ){
					select.selectedIndex = titles.length;
					nostyle.className = "enable";
				} else {
					nostyle.className = "disable";
				}
			}
			
			/* テキストを追加 */
			if( PSG.Config.StyleSheet.text ){
				var label = PSG.DOM.createElement("label");
				label.setAttribute("for", PSG.StyleSheet.selectID ); // Feedback from ALIMIKA SATOMI 2004-12-19
				label.appendChild( document.createTextNode(PSG.Config.StyleSheet.text) );
				div.appendChild(label);
			}
			div.appendChild(select);
			selectForm.appendChild(div);
			
			PSG.Event.addEvent( select, "change", PSG.StyleSheet.handleStyleSheet );
			try{
				var targetElement;
				if( PSG.Config.StyleSheet.targetElementID ){
					targetElement = document.getElementById(PSG.Config.StyleSheet.targetElementID);
				} else {
					targetElement = document.getElementsByTagName(PSG.Config.StyleSheet.targetElementName)[PSG.Config.StyleSheet.targetElementIndex-1];
				}
				PSG.DOM.addElement( selectForm, targetElement, PSG.Config.StyleSheet.isBefore, PSG.Config.StyleSheet.isOut );
			}catch(err){}
		},
		
		/*
		@access : public
		@return : void
		@description :
		以前のスタイルシート設定を読み込んで、そのスタイルを反映させます。
		クッキーがなんらかの理由で存在しなければ何もしません。
		*/
		init : function(){
			var sheet = PSG.Cookie.getCookie("sheet");
			if( sheet != null ){
				PSG.StyleSheet.changeStyleSheet(sheet);
			}
		}
	}
}

/*
スタイル切り替え機能だけしかないのに
それを使わないという人はいないので無条件で実行
*/
PSG.Event.addLoadEvent( PSG.StyleSheet.createController );
PSG.StyleSheet.init(); // 表示スタイルをレンダリング開始前に切り替える

